Add QA tests and expand serialization for API

This commit is contained in:
Jon Evans 2024-01-20 18:35:29 -05:00
parent 6bd02cae6d
commit 1dbe78c68b
106 changed files with 9643 additions and 736 deletions

View File

@ -22,11 +22,19 @@ set( KIAPI_PROTO_SRCS
common/envelope.proto
common/types/base_types.proto
common/types/enums.proto
common/types/project_settings.proto
common/commands/base_commands.proto
common/commands/editor_commands.proto
common/commands/project_commands.proto
board/board.proto
board/board_commands.proto
board/board_types.proto
schematic/schematic_types.proto
schematic/schematic_commands.proto
)
# Generated C++ code must be in the build dir; it is dependent on the version of protoc installed
@ -101,18 +109,3 @@ target_include_directories( kiapi INTERFACE
# Because when building internally, the generated files do not include the "api" base path
target_include_directories( kiapi PUBLIC ${KIAPI_CPP_BASEPATH} )
option( KICAD_BUILD_ENUM_EXPORTER
"Build the enum exporter used as part of generating the IPC APIs"
OFF )
if( KICAD_BUILD_ENUM_EXPORTER )
add_subdirectory( enums )
add_custom_target( enum_definitions
COMMAND $<TARGET_FILE:enum_exporter> ${CMAKE_CURRENT_BINARY_DIR}/enums.json
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating API definitions from KiCad enums..."
DEPENDS enum_exporter
)
endif()

View File

@ -1,91 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <filesystem>
#include <iostream>
#include <string>
#include <argparse/argparse.hpp>
#include <fmt.h>
#include <nlohmann/json.hpp>
#define MAGIC_ENUM_RANGE_MAX 1024
#include <magic_enum.hpp>
#include <layer_ids.h>
#include <eda_shape.h>
#include <core/typeinfo.h>
template<typename T>
nlohmann::json FormatEnum()
{
nlohmann::json js;
js["type"] = magic_enum::enum_type_name<T>();
js["values"] = nlohmann::json::array();
for( const std::pair<T, std::string_view>& entry : magic_enum::enum_entries<T>() )
{
js["values"].emplace_back( nlohmann::json( {
{ "key", entry.second },
{ "value", static_cast<int>( entry.first ) }
} ) );
}
return js;
}
int main( int argc, char* argv[] )
{
argparse::ArgumentParser args( "enum_exporter" );
args.add_argument( "output_dir" ).default_value( std::string() );
try
{
args.parse_args( argc, argv );
}
catch( const std::runtime_error& err )
{
std::cerr << err.what() << std::endl;
std::cerr << args;
std::exit( 1 );
}
std::filesystem::path path( args.get<std::string>( "output_dir" ) );
std::ofstream outfile;
if( !path.empty() )
{
path = std::filesystem::absolute( path );
outfile.open( path );
}
std::ostream& out = outfile.is_open() ? outfile : std::cout;
nlohmann::json js = nlohmann::json::array();
js += FormatEnum<PCB_LAYER_ID>();
js += FormatEnum<SHAPE_T>();
js += FormatEnum<KICAD_T>();
out << js.dump( 4 ) << std::endl;
}

116
api/proto/board/board.proto Normal file
View File

@ -0,0 +1,116 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.board;
import "common/types/base_types.proto";
import "board/board_types.proto";
message BoardFinish
{
string type_name = 1;
}
message BoardImpedanceControl
{
bool is_controlled = 1;
}
message BoardEdgeConnector
{
}
message Castellation
{
bool has_castellated_pads = 1;
}
message EdgePlating
{
bool has_edge_plating = 1;
}
message BoardEdgeSettings
{
BoardEdgeConnector connector = 1;
Castellation castellation = 2;
EdgePlating plating = 3;
}
message BoardStackupCopperLayer
{
}
message BoardStackupLayer
{
kiapi.common.types.Distance thickness = 1;
kiapi.board.types.BoardLayer layer = 2;
bool enabled = 3;
oneof item {
BoardStackupCopperLayer copper = 4;
}
}
message BoardStackup
{
BoardFinish finish = 1;
BoardImpedanceControl impedance = 2;
// NOTE: m_HasThicknessConstrains appears to be unused
BoardEdgeSettings edge = 3;
repeated BoardStackupLayer layers = 4;
}
// LAYER_CLASS_* in BOARD_DESIGN_SETTINGS -- needs to become an enum class
enum BoardLayerClass
{
BLC_UNKNOWN = 0;
BLC_SILKSCREEN = 1;
BLC_COPPER = 2;
BLC_EDGES = 3;
BLC_COURTYARD = 4;
BLC_FABRICATION = 5;
BLC_OTHER = 6;
}
message BoardLayerGraphicsDefaults
{
BoardLayerClass layer = 1;
kiapi.common.types.TextAttributes text = 2;
kiapi.common.types.Distance line_thickness = 3;
}
message GraphicsDefaults
{
repeated BoardLayerGraphicsDefaults layers = 1;
}
// Anything that isn't stackup or design rules
message BoardSettings
{
GraphicsDefaults graphics_defaults = 1;
// Dimension default settings
}
message BoardDesignRules
{
}

View File

@ -0,0 +1,151 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.board.commands;
import "common/types/base_types.proto";
import "common/types/enums.proto";
import "board/board.proto";
import "board/board_types.proto";
/*
* Board stackup and properties
*/
message GetBoardStackup
{
kiapi.common.types.DocumentSpecifier board = 1;
}
message BoardStackupResponse
{
kiapi.board.BoardStackup stackup = 1;
}
message UpdateBoardStackup
{
kiapi.common.types.DocumentSpecifier board = 1;
kiapi.board.BoardStackup stackup = 2;
}
message GetGraphicsDefaults
{
kiapi.common.types.DocumentSpecifier board = 1;
}
message GraphicsDefaultsResponse
{
kiapi.board.GraphicsDefaults defaults = 1;
}
/*
* Net management
*/
message GetNets
{
kiapi.common.types.DocumentSpecifier board = 1;
// If provided, will only return nets that belong to the given netclass.
// If more than one netclass_filter is given, nets belonging to any of the given classes will
// be returned.
repeated string netclass_filter = 2;
}
message NetsResponse
{
repeated kiapi.board.types.Net nets = 1;
}
// Retrieve all the copper items belonging to a certain net or set of nets
// returns kiapi.common.commands.GetItemsResponse
message GetItemsByNet
{
// Specifies which document to query, which fields to return, etc.
kiapi.common.types.ItemHeader header = 1;
// List of one or more types of items to retreive
repeated kiapi.common.types.KiCadObjectType types = 2;
// A list of net codes to filter items by
repeated kiapi.board.types.NetCode net_codes = 3;
}
// Retrieve all the copper items belonging to a certain net class or set of net classes
// returns kiapi.common.commands.GetItemsResponse
message GetItemsByNetClass
{
// Specifies which document to query, which fields to return, etc.
kiapi.common.types.ItemHeader header = 1;
// List of one or more types of items to retreive
repeated kiapi.common.types.KiCadObjectType types = 2;
// A list of net class names to filter items by
repeated string net_classes = 3;
}
/*
* Blocking operations
*/
// Refills some or all zones on the board.
// This is a blocking operation; it will return Empty immediately, but KiCad will return
// ApiStatusCode.AS_BUSY to all future API requests until the zone fill has completed.
message RefillZones
{
kiapi.common.types.DocumentSpecifier board = 1;
// A list of zones to refill. If empty, all zones are refilled.
repeated kiapi.common.types.KIID zones = 2;
}
/*
* Utilities
*/
// returns kiapi.common.commands.BoundingBoxResponse
message GetTextExtents
{
// A temporary text item to calculate the bounding box for
kiapi.board.types.Text text = 1;
}
//// Interactive commands ////
// These commands begin an interactive operation in the editor.
// They return a response immediately, but the editor will become busy
// and will not reply to further API commands until the user has finished
// the operation.
// These commands will return an error if received in a non-interactive context.
// Selects and begins an interactive move of the given item(s).
// NOTE: Takes ownership of the active commit, if one exists:
// the move tool will push the commit when the user confirms the move,
// or roll back the commit if the user cancels the move. Keep this in
// mind if using this command in combination with commands that create
// or modify items using an explicit commit.
message InteractiveMoveItems
{
kiapi.common.types.DocumentSpecifier board = 1;
repeated kiapi.common.types.KIID items = 2;
}

View File

@ -21,90 +21,467 @@ syntax = "proto3";
package kiapi.board.types;
import "google/protobuf/any.proto";
import "common/types/base_types.proto";
/// Represents a track segment on a board
enum BoardLayer
{
BL_UNKNOWN = 0;
BL_UNDEFINED = 1;
BL_UNSELECTED = 2;
BL_F_Cu = 3;
BL_In1_Cu = 4;
BL_In2_Cu = 5;
BL_In3_Cu = 6;
BL_In4_Cu = 7;
BL_In5_Cu = 8;
BL_In6_Cu = 9;
BL_In7_Cu = 10;
BL_In8_Cu = 11;
BL_In9_Cu = 12;
BL_In10_Cu = 13;
BL_In11_Cu = 14;
BL_In12_Cu = 15;
BL_In13_Cu = 16;
BL_In14_Cu = 17;
BL_In15_Cu = 18;
BL_In16_Cu = 19;
BL_In17_Cu = 20;
BL_In18_Cu = 21;
BL_In19_Cu = 22;
BL_In20_Cu = 23;
BL_In21_Cu = 24;
BL_In22_Cu = 25;
BL_In23_Cu = 26;
BL_In24_Cu = 27;
BL_In25_Cu = 28;
BL_In26_Cu = 29;
BL_In27_Cu = 30;
BL_In28_Cu = 31;
BL_In29_Cu = 32;
BL_In30_Cu = 33;
BL_B_Cu = 34;
BL_B_Adhes = 35;
BL_F_Adhes = 36;
BL_B_Paste = 37;
BL_F_Paste = 38;
BL_B_SilkS = 39;
BL_F_SilkS = 40;
BL_B_Mask = 41;
BL_F_Mask = 42;
BL_Dwgs_User = 43;
BL_Cmts_User = 44;
BL_Eco1_User = 45;
BL_Eco2_User = 46;
BL_Edge_Cuts = 47;
BL_Margin = 48;
BL_B_CrtYd = 49;
BL_F_CrtYd = 50;
BL_B_Fab = 51;
BL_F_Fab = 52;
BL_User_1 = 53;
BL_User_2 = 54;
BL_User_3 = 55;
BL_User_4 = 56;
BL_User_5 = 57;
BL_User_6 = 58;
BL_User_7 = 59;
BL_User_8 = 60;
BL_User_9 = 61;
}
message NetCode
{
int32 value = 1;
}
// Describes a copper item's net
message Net
{
// A unique code representing this net
NetCode code = 1;
// Human-readable net name
string name = 2;
}
// Represents a track segment on a board
message Track
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Point2D start = 2;
kiapi.common.types.Point2D end = 3;
kiapi.common.types.Vector2 start = 2;
kiapi.common.types.Vector2 end = 3;
kiapi.common.types.Distance width = 4;
kiapi.common.types.LockedState locked = 5;
kiapi.common.types.BoardLayer layer = 6;
kiapi.common.types.Net net = 7;
BoardLayer layer = 6;
Net net = 7;
}
/// Represents an arc track (not a PCB_SHAPE in arc shape)
/// Arc tracks in KiCad store start, midpoint, and end.
/// All other values (center point, angles, etc) are inferred.
// Represents an arc track (not a PCB_SHAPE in arc shape)
// Arc tracks in KiCad store start, midpoint, and end.
// All other values (center point, angles, etc) are inferred.
message Arc
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Point2D start = 2;
kiapi.common.types.Point2D mid = 3; /// Arc midpoint
kiapi.common.types.Point2D end = 4;
kiapi.common.types.Vector2 start = 2;
kiapi.common.types.Vector2 mid = 3; // Arc midpoint
kiapi.common.types.Vector2 end = 4;
kiapi.common.types.Distance width = 5;
kiapi.common.types.LockedState locked = 6;
kiapi.common.types.BoardLayer layer = 7;
kiapi.common.types.Net net = 8;
BoardLayer layer = 7;
Net net = 8;
}
enum PadStackType
{
PST_UNKNOWN = 0;
PST_THROUGH = 1; /// Through all layers; same shape on all layers
PST_BLIND_BURIED = 2; /// From a start layer to end layer (inclusive); same shape on all included layers
PST_THROUGH = 1; // Through all layers; same shape on all layers
PST_BLIND_BURIED = 2; // From a start layer to end layer (inclusive); same shape on all included layers
}
enum UnconnectedLayerRemoval
{
ULR_UNKNOWN = 0;
/// Keep annular rings on all layers
// Keep annular rings on all layers
ULR_KEEP = 1;
/// Remove annular rings on unconnected layers, including start and end layers.
// Remove annular rings on unconnected layers, including start and end layers.
ULR_REMOVE = 2;
/// Remove annular rings on unconnected layers, but preserve start and end layers even if unconnected.
// Remove annular rings on unconnected layers, but preserve start and end layers even if unconnected.
ULR_REMOVE_EXCEPT_START_AND_END = 3;
}
/// A pad stack definition for a multilayer pad or via.
// The shape of a pad on a given layer
enum PadStackShape
{
PSS_UNKNOWN = 0;
PSS_CIRCLE = 1;
PSS_RECTANGLE = 2;
PSS_OVAL = 3;
PSS_TRAPEZOID = 4;
PSS_ROUNDRECT = 5;
PSS_CHAMFEREDRECT = 6;
PSS_CUSTOM = 7;
}
// Which corners are chamfered in a PSS_CHAMFEREDRECT
message ChamferedRectCorners
{
bool top_left = 1;
bool top_right = 2;
bool bottom_left = 3;
bool bottom_right = 4;
}
// The defintion of a padstack on a single layer
message PadStackLayer
{
// The board layers of this padstack entry
repeated BoardLayer layers = 1;
// The shape of the pad on this layer
PadStackShape shape = 2;
// The size (x and y) of the shape on this layer
kiapi.common.types.Vector2 size = 3;
// How much to round the corners of the shape by, as a fraction of min(size.x, size.y)
// Only used for PSS_ROUNDRECT or PSS_CHAMFEREDRECT
float corner_rounding_ratio = 4;
// How much to round the corners of the shape by, as a fraction of min(size.x, size.y)
// Only used for PSS_CHAMFEREDRECT
float chamfer_ratio = 5;
ChamferedRectCorners chamfered_corners = 6;
repeated GraphicShape custom_shapes = 7;
}
// A pad stack definition for a multilayer pad or via.
message PadStack
{
/// What type of pad stack this represents.
// What type of pad stack this represents.
PadStackType type = 1;
/// Lowest (closest to F_Cu) layer this stack exists on. Ignored if type == PST_THROUGH.
kiapi.common.types.BoardLayer start_layer = 2;
// Lowest (closest to F_Cu) layer this stack exists on. Ignored if type == PST_THROUGH.
BoardLayer start_layer = 2;
/// Highest (closest to B_Cu) layer this stack exists on. Ignored if type == PST_THROUGH.
kiapi.common.types.BoardLayer end_layer = 3;
// Highest (closest to B_Cu) layer this stack exists on. Ignored if type == PST_THROUGH.
BoardLayer end_layer = 3;
/// How to treat annular rings on unconnected layers.
// How to treat pad shapes on unconnected layers.
UnconnectedLayerRemoval unconnected_layer_removal = 4;
// The diameter, in x and y, of the pad's drilled hole, if this pad has a hole.
// x and y will be the same value if the hole is round.
kiapi.common.types.Vector2 drill_diameter = 5;
repeated PadStackLayer layers = 6;
// The overall rotation of this padstack (affects all layers)
kiapi.common.types.Angle angle = 7;
}
/// Represents a via
// Represents a via
message Via
{
/// The unique identifier of the via
// The unique identifier of the via
kiapi.common.types.KIID id = 1;
/// The location of the via's center point
kiapi.common.types.Point2D position = 2;
// The location of the via's center point
kiapi.common.types.Vector2 position = 2;
/// The diameter of the via's circular copper pad
kiapi.common.types.Distance pad_diameter = 4;
// The pad stack definition for this via. The via's VIATYPE (blind/buried/normal) is inferred from this.
PadStack pad_stack = 3;
/// The diameter of the via's drilled hole
kiapi.common.types.Distance drill_diameter = 5;
kiapi.common.types.LockedState locked = 4;
/// The pad stack definition for this via. The via's VIATYPE (blind/buried/normal) is inferred from this.
Net net = 5;
}
message GraphicSegmentAttributes
{
kiapi.common.types.Vector2 start = 1;
kiapi.common.types.Vector2 end = 2;
}
message GraphicRectangleAttributes
{
kiapi.common.types.Vector2 top_left = 1;
kiapi.common.types.Vector2 bottom_right = 2;
}
message GraphicArcAttributes
{
kiapi.common.types.Vector2 start = 1;
kiapi.common.types.Vector2 mid = 2;
kiapi.common.types.Vector2 end = 3;
}
message GraphicCircleAttributes
{
kiapi.common.types.Vector2 center = 1;
// A point on the radius of the circle. This is stored instead of just a radius so that the point
// by which the user can adjust the circle radius is persisted.
kiapi.common.types.Vector2 radius_point = 2;
}
message GraphicBezierAttributes
{
kiapi.common.types.Vector2 start = 1;
kiapi.common.types.Vector2 control1 = 2;
kiapi.common.types.Vector2 control2 = 3;
kiapi.common.types.Vector2 end = 4;
}
message GraphicShape
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.LockedState locked = 2;
BoardLayer layer = 3;
Net net = 4;
kiapi.common.types.GraphicAttributes attributes = 5;
oneof geometry {
GraphicSegmentAttributes segment = 6;
GraphicRectangleAttributes rectangle = 7;
GraphicArcAttributes arc = 8;
GraphicCircleAttributes circle = 9;
kiapi.common.types.PolySet polygon = 10;
GraphicBezierAttributes bezier = 11;
}
}
// A board-specific text object, existing on a board layer
message Text
{
kiapi.common.types.Text text = 1;
BoardLayer layer = 2;
}
// A board-specific textbox, existing on a board layer
message TextBox
{
kiapi.common.types.TextBox textbox = 1;
BoardLayer layer = 2;
}
// NOTE: There has been some discussion about what to do with pad attributes and properties.
// This may be considered somewhat unstable until we decide what to do with the KiCad side.
// It is not clear what the set of mutually-exclusive pad types will be at the end of the day,
// versus what will be non-exclusive attributes/properties.
// For now, this maps to PAD_ATTRIB in KiCad.
enum PadType
{
PT_UNKNOWN = 0;
PT_PTH = 1;
PT_SMD = 2;
PT_EDGE_CONNECTOR = 3;
PT_NPTH = 4;
}
enum CustomPadShapeZoneFillStrategy
{
CPSZ_UNKNOWN = 0;
CPSZ_OUTLINE = 1;
CPSZ_CONVEXHULL = 2;
}
message ThermalSpokeSettings
{
int64 width = 1;
kiapi.common.types.Angle angle = 2;
int64 gap = 3;
}
message Pad
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.LockedState locked = 2;
string number = 3;
Net net = 4;
PadType type = 5;
PadStack pad_stack = 6;
kiapi.common.types.LockedState locked = 7;
kiapi.common.types.Net net = 8;
// A pad's position is always relative to the parent footprint's origin
kiapi.common.types.Vector2 position = 7;
DesignRuleOverrides overrides = 8;
ThermalSpokeSettings thermal_spokes = 9;
}
// Copper zone, non-copper zone, or rule area
message Zone
{
// TODO
}
message Dimension
{
// TODO
}
message ReferenceImage
{
// TODO
}
message Group
{
// TODO
}
message FieldId
{
int32 id = 1;
}
message Field
{
FieldId id = 1;
string name = 2;
Text text = 3;
}
message Model3D
{
// TODO
}
enum FootprintMountingStyle
{
FMS_UNKNOWN = 0;
FMS_THROUGH_HOLE = 1;
FMS_SMD = 2;
FMS_UNSPECIFIED = 3;
}
message FootprintAttributes
{
string description = 1;
string keywords = 2;
bool not_in_schematic = 3;
bool exclude_from_position_files = 4;
bool exclude_from_bill_of_materials = 5;
bool exempt_from_courtyard_requirement = 6;
bool do_not_populate = 7;
FootprintMountingStyle mounting_style = 8;
}
// enum class ZONE_CONNECTION
enum ZoneConnectionStyle
{
ZCS_UNKNOWN = 0;
ZCS_INHERITED = 1;
ZCS_NONE = 2;
ZCS_THERMAL = 3;
ZCS_FULL = 4;
ZCS_PTH_THERMAL = 5; // Thermal reliefs for plated through holes, solid for SMD pads
}
message DesignRuleOverrides
{
// Copper-to-copper clearance override
kiapi.common.types.Distance clearance = 1;
// Solder mask expansion/contraction
kiapi.common.types.Distance solder_mask_margin = 2;
// Solder paste expansion/contraction
kiapi.common.types.Distance solder_paste_margin = 3;
// Solder paste expansion/contraction ratio
kiapi.common.types.Ratio solder_paste_margin_ratio = 4;
ZoneConnectionStyle zone_connection = 5;
}
message NetTieDefinition
{
repeated string pad_number = 1;
}
// A footprint definition (i.e. what would be in a library)
message Footprint
{
kiapi.common.types.LibraryIdentifier id = 1;
kiapi.common.types.Vector2 anchor = 2;
FootprintAttributes attributes = 3;
DesignRuleOverrides overrides = 4;
repeated NetTieDefinition net_ties = 5;
repeated BoardLayer private_layers = 6;
Field reference_field = 7;
Field value_field = 8;
Field datasheet_field = 9;
Field description_field = 10;
// All footprint items except for mandatory fields
repeated google.protobuf.Any items = 11;
}
// An instance of a footprint on a board
message FootprintInstance
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 position = 2;
kiapi.common.types.Angle orientation = 3;
BoardLayer layer = 4;
kiapi.common.types.LockedState locked = 5;
Footprint definition = 6;
Field reference_field = 7;
Field value_field = 8;
Field datasheet_field = 9;
Field description_field = 10;
FootprintAttributes attributes = 11;
DesignRuleOverrides overrides = 12;
}

View File

@ -31,3 +31,8 @@ message GetVersionResponse
{
kiapi.common.types.KiCadVersion version = 1;
}
// A command to check if the connection to KiCad is OK
message Ping
{
}

View File

@ -27,17 +27,18 @@ package kiapi.common.commands;
import "google/protobuf/any.proto";
import "common/types/base_types.proto";
import "common/types/enums.proto";
/// Refreshes the given frame, if that frame is open
// Refreshes the given frame, if that frame is open
message RefreshEditor
{
kiapi.common.types.FrameType frame = 1;
}
/// Retrieves a list of open documents of the given type
// Retrieves a list of open documents of the given type
message GetOpenDocuments
{
/// Which type of documents to query
// Which type of documents to query
kiapi.common.types.DocumentType type = 1;
}
@ -86,140 +87,150 @@ message BeginCommit
message BeginCommitResponse
{
// Opaque identifier tracking a commit
kiapi.common.types.KIID id = 1;
}
enum CommitResult
enum CommitAction
{
CR_UNKNOWN = 0;
CR_OK = 1; // Commit was pushed successfully
CR_NO_COMMIT = 2; // There was no commit started
CMA_UNKNOWN = 0;
CMA_COMMIT = 1; // Commit the changes to the design
CMA_DROP = 2; // Cancel this commit
}
message EndCommit
{
// The ID that was given by BeginCommit
kiapi.common.types.KIID id = 1;
// What to do with this commit
CommitAction action = 2;
// Optional message describing this changeset
string message = 1;
string message = 3;
}
message EndCommitResponse
{
CommitResult result = 1;
}
/// Creates new items on a given document
// Creates new items on a given document
message CreateItems
{
/// Specifies which document to create on, which fields are included, etc.
// Specifies which document to create on, which fields are included, etc.
kiapi.common.types.ItemHeader header = 1;
/// List of items to create
// List of items to create
repeated google.protobuf.Any items = 2;
/// Items may be created on a top-level document (sheet, board, etc) or inside a container
/// (symbol, footprint). If this field is not empty, it holds the ID of a symbol or footprint
/// that the items should be added to. This ID must be an existing symbol (for schematic
/// documents) or footprint (for board documents). If the given container does not exist or is
/// not the correct item type, the CreateItems call will fail.
// Items may be created on a top-level document (sheet, board, etc) or inside a container
// (symbol, footprint). If this field is not empty, it holds the ID of a symbol or footprint
// that the items should be added to. This ID must be an existing symbol (for schematic
// documents) or footprint (for board documents). If the given container does not exist or is
// not the correct item type, the CreateItems call will fail.
kiapi.common.types.KIID container = 3;
}
enum ItemCreationStatus
enum ItemStatusCode
{
ICS_UNKNOWN = 0;
ICS_OK = 1; /// The item was created
ICS_INVALID_TYPE = 2; /// The item's type is not valid for the given document
ICS_EXISTING = 3; /// The item had a specified KIID and that KIID was already in use
ISC_UNKNOWN = 0;
ISC_OK = 1; // The item was created or updated
ISC_INVALID_TYPE = 2; // The item's type is not valid for the given document
ISC_EXISTING = 3; // The item to be created had a specified KIID and that KIID was already in use
ISC_NONEXISTENT = 4; // The item to be updated did not exist in the given document
ISC_IMMUTABLE = 5; // The item to be updated is not allowed to be modified by the API
ISC_INVALID_DATA = 7; // The item to be created does not have valid data for the given document
}
// Per-item status feedback for creation and update calls
message ItemStatus
{
ItemStatusCode code = 1;
string error_message = 2;
}
message ItemCreationResult
{
ItemCreationStatus status = 1;
ItemStatus status = 1;
/// The created version of the item, including an updated KIID as applicable
// The created version of the item, including an updated KIID as applicable
google.protobuf.Any item = 2;
}
message CreateItemsResponse
{
/// Specifies which document was modified, which fields are included in created_items, etc.
// Specifies which document was modified, which fields are included in created_items, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were created
// Status of the overall request; may return IRS_OK even if no items were created
kiapi.common.types.ItemRequestStatus status = 2;
/// Status of each item to be created
// Status of each item to be created
repeated ItemCreationResult created_items = 3;
}
message GetItems
{
/// Specifies which document to query, which fields to return, etc.
// Specifies which document to query, which fields to return, etc.
kiapi.common.types.ItemHeader header = 1;
/// List of one or more types of items to retreive
repeated kiapi.common.types.ItemType types = 2;
// List of one or more types of items to retreive
repeated kiapi.common.types.KiCadObjectType types = 2;
}
message GetItemsResponse
{
/// Specifies which document was modified, which fields are included in items, etc.
// Specifies which document was modified, which fields are included in items, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were retrieved
// Status of the overall request; may return IRS_OK even if no items were retrieved
kiapi.common.types.ItemRequestStatus status = 2;
repeated google.protobuf.Any items = 3;
}
/// Updates items in a given document
// Updates items in a given document
message UpdateItems
{
/// Specifies which document to modify, which fields are included, etc.
// Specifies which document to modify, which fields are included, etc.
kiapi.common.types.ItemHeader header = 1;
/// List of items to modify
// List of items to modify
repeated google.protobuf.Any items = 2;
}
enum ItemUpdateStatus
{
IUS_UNKNOWN = 0;
IUS_OK = 1; /// The item was updated
IUS_INVALID_TYPE = 2; /// The item's type is not valid for the given document
IUS_NONEXISTENT = 3; /// The item did not exist in the given document
IUS_IMMUTABLE = 4; /// The item is not allowed to be modified by the API
}
message ItemUpdateResult
{
ItemUpdateStatus status = 1;
ItemStatus status = 1;
/// The update version of the item
// The update version of the item
google.protobuf.Any item = 2;
}
message UpdateItemsResponse
{
/// Specifies which document was modified, which fields are included in updated_items, etc.
// Specifies which document was modified, which fields are included in updated_items, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were modified
// Status of the overall request; may return IRS_OK even if no items were modified
kiapi.common.types.ItemRequestStatus status = 2;
/// Status of each item to be created
// Status of each item to be created
repeated ItemUpdateResult updated_items = 3;
}
/// Deletes items in a given document
// Deletes items in a given document
message DeleteItems
{
/// Specifies which document to modify
// Specifies which document to modify
kiapi.common.types.ItemHeader header = 1;
/// List of item KIIDs to delete
// List of item KIIDs to delete
repeated kiapi.common.types.KIID item_ids = 2;
}
@ -227,8 +238,8 @@ enum ItemDeletionStatus
{
IDS_UNKNOWN = 0;
IDS_OK = 1;
IDS_NONEXISTENT = 2; /// The item did not exist in the given document
IDS_IMMUTABLE = 3; /// The item is not allowed to be modified by the API
IDS_NONEXISTENT = 2; // The item did not exist in the given document
IDS_IMMUTABLE = 3; // The item is not allowed to be modified by the API
}
message ItemDeletionResult
@ -240,12 +251,49 @@ message ItemDeletionResult
message DeleteItemsResponse
{
/// Specifies which document was modified, etc.
// Specifies which document was modified, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were deleted
// Status of the overall request; may return IRS_OK even if no items were deleted
kiapi.common.types.ItemRequestStatus status = 2;
/// Status of each item requested to be deleted
// Status of each item requested to be deleted
repeated ItemDeletionResult deleted_items = 3;
}
message GetItemBoundingBox
{
kiapi.common.types.ItemHeader header = 1;
kiapi.common.types.KIID id = 2;
}
message BoundingBoxResponse
{
kiapi.common.types.Vector2 position = 1;
kiapi.common.types.Vector2 size = 2;
}
// Tests if a certain point falls within tolerance of an item's geometry
message HitTest
{
kiapi.common.types.ItemHeader header = 1;
kiapi.common.types.KIID id = 2;
kiapi.common.types.Vector2 position = 3;
int32 tolerance = 4;
}
enum HitTestResult
{
HTR_UNKNOWN = 0;
HTR_NO_HIT = 1;
HTR_HIT = 2;
}
message HitTestResponse
{
HitTestResult result = 1;
}

View File

@ -0,0 +1,34 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.common.commands;
import "common/types/project_settings.proto";
message GetNetClasses
{
}
message NetClassesResponse
{
repeated kiapi.common.project.NetClass net_classes = 1;
}

View File

@ -29,9 +29,11 @@ enum ApiStatusCode
AS_OK = 1; // Request succeeded
AS_TIMEOUT = 2; // Request timed out
AS_BAD_REQUEST = 3; // The request had invalid parameters or otherwise was illegal
AS_NOT_READY = 4; // KiCad was not (yet) in a state where it could handle API requests
AS_NOT_READY = 4; // KiCad has recently started and cannot handle API requests yet
AS_UNHANDLED = 5; // The request was not handled by KiCad
AS_TOKEN_MISMATCH = 6; // The kicad_token in the request didn't match this KiCad's token
AS_BUSY = 7; // KiCad is busy performing an operation and can't accept API commands
AS_UNIMPLEMENTED = 8; // The requested API call has not yet been implemented
}
/*

View File

@ -27,6 +27,7 @@ syntax = "proto3";
package kiapi.common.types;
import "google/protobuf/field_mask.proto";
import "common/types/enums.proto";
enum CommandStatus
{
@ -95,10 +96,10 @@ enum DocumentType
*/
message LibraryIdentifier
{
/// The library portion of the LIB_ID
// The library portion of the LIB_ID
string library_nickname = 1;
/// The symbol or footprint name
// The symbol or footprint name
string entry_name = 2;
}
@ -107,13 +108,25 @@ message LibraryIdentifier
*/
message SheetPath
{
/// The canonical path to the sheet. The first KIID will be the root sheet, etc.
// The canonical path to the sheet. The first KIID will be the root sheet, etc.
repeated KIID path = 1;
/// The path converted to a human readable form such as "/", "/child", or "/child/grandchild"
// The path converted to a human readable form such as "/", "/child", or "/child/grandchild"
string path_human_readable = 2;
}
/**
* Describes a KiCad project
*/
message ProjectSpecifier
{
// The name of the project (without the kicad_pro extension)
string name = 1;
// The path to the project directory
string path = 2;
}
/**
* Describes a document that will be the target of a request
*/
@ -123,24 +136,17 @@ message DocumentSpecifier
oneof identifier
{
/// If type == DT_SYMBOL or DT_FOOTPRINT, identifies a certain library entry
// If type == DT_SYMBOL or DT_FOOTPRINT, identifies a certain library entry
LibraryIdentifier lib_id = 2;
/// If type == DT_SCHEMATIC, identifies a sheet with a given path
// If type == DT_SCHEMATIC, identifies a sheet with a given path
SheetPath sheet_path = 3;
/// If type == DT_PCB, identifies a PCB with a given filename, e.g. "board.kicad_pcb"
// If type == DT_PCB, identifies a PCB with a given filename, e.g. "board.kicad_pcb"
string board_filename = 4;
}
}
/**
* Describes the type of a KiCad item (wrapper for KICAD_T)
*/
message ItemType
{
/// Must be a valid value in the KICAD_T C++ enum (see typeinfo.h)
int32 type = 1;
ProjectSpecifier project = 5;
}
/**
@ -148,11 +154,15 @@ message ItemType
*/
message ItemHeader
{
/// Which document is this request targeting?
// Which document is this request targeting?
DocumentSpecifier document = 1;
/// Which fields on the item(s) are included with this request or response
google.protobuf.FieldMask field_mask = 2;
// Which container within the document is this request targeting?
// If container is omitted or empty, the document is used as the container.
KIID container = 2;
// Which fields on the item(s) are included with this request or response
google.protobuf.FieldMask field_mask = 3;
}
/**
@ -162,32 +172,93 @@ enum ItemRequestStatus
{
IRS_UNKNOWN = 0;
IRS_OK = 1;
IRS_DOCUMENT_NOT_FOUND = 2; /// The given document is not open in KiCad
IRS_FIELD_MASK_INVALID = 3; /// The given field_mask contains invalid specifiers
IRS_DOCUMENT_NOT_FOUND = 2; // The given document is not open in KiCad
IRS_FIELD_MASK_INVALID = 3; // The given field_mask contains invalid specifiers
}
/// Describes a point in 2D space. All coordinates are in nanometers.
message Point2D
// Describes a point or distance in 2D space. All coordinates are in nanometers.
message Vector2
{
int64 x_nm = 1;
int64 y_nm = 2;
}
/// Describes a point in 3D space. All coordinates are in nanometers.
message Point3D
// Describes a point or distance in 3D space. All coordinates are in nanometers.
message Vector3
{
int64 x_nm = 1;
int64 y_nm = 2;
int64 z_nm = 3;
}
/// Describes a quantity of distance (size, length, etc). All coordinates are in nanometers.
// Describes a quantity of distance (size, length, etc). All coordinates are in nanometers.
message Distance
{
int64 value_nm = 1;
}
/// Describes whether or not an item is locked for editing or movement
// Corresponds to EDA_ANGLE, where the underlying storage is degrees
message Angle
{
double value_degrees = 1;
}
// Represents a value from 0.0 to 1.0
message Ratio
{
double value = 1;
}
// Corresponds to COLOR4D. Each color channel is a double from 0.0 to 1.0.
message Color
{
double r = 1;
double g = 2;
double b = 3;
double a = 4;
}
// The formulation of arc that is used in KiCad core geometry code.
// Start, midpoint (on the arc) and end are stored. Angle, center point, etc are calculated.
message ArcStartMidEnd
{
Vector2 start = 1;
Vector2 mid = 2;
Vector2 end = 3;
}
message PolyLineNode
{
oneof geometry {
Vector2 point = 1;
ArcStartMidEnd arc = 2;
}
}
// Corresponds to class SHAPE_LINE_CHAIN: A closed or open polyline that can include arcs.
// For non-arc cases, each node is a point along the line. An implicit line segment exists
// between the last and first node if closed is true. When arcs are present, the arc start and
// end points are not duplicated by point nodes (meaning, for example, a rectangle with rounded
// corners could be represented with four arc nodes and no point nodes).
message PolyLine
{
repeated PolyLineNode nodes = 1;
bool closed = 2;
}
message PolygonWithHoles
{
PolyLine outline = 1;
repeated PolyLine holes = 2;
}
// Corresponds to SHAPE_POLY_SET: a set of polygons or polylines
message PolySet
{
repeated PolygonWithHoles polygons = 1;
}
// Describes whether or not an item is locked for editing or movement
enum LockedState
{
LS_UNKNOWN = 0;
@ -195,23 +266,69 @@ enum LockedState
LS_LOCKED = 2;
}
message BoardLayer
message TextAttributes
{
int32 layer_id = 1; /// From PCB_LAYER_T
string font_name = 1;
HorizontalAlignment horizontal_alignment = 2;
VerticalAlignment vertical_alignment = 3;
kiapi.common.types.Angle angle = 4;
double line_spacing = 5;
kiapi.common.types.Distance stroke_width = 6;
bool italic = 7;
bool bold = 8;
bool underlined = 9;
bool visible = 10;
bool mirrored = 11;
bool multiline = 12;
bool keep_upright = 13;
kiapi.common.types.Vector2 size = 14;
}
/// Describes a copper item's net
message Net
message Text
{
/// A unique code representing this net
int32 code = 1;
/// Human-readable net name
string name = 2;
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 position = 2;
kiapi.common.types.TextAttributes attributes = 3;
kiapi.common.types.LockedState locked = 4;
string text = 5;
string hyperlink = 6;
bool knockout = 7;
}
/// Describes a net class (a grouping of nets)
message NetClass
message TextBox
{
string name = 1;
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 top_left = 2;
kiapi.common.types.Vector2 bottom_right = 3;
kiapi.common.types.TextAttributes attributes = 4;
kiapi.common.types.LockedState locked = 5;
string text = 6;
}
message StrokeAttributes
{
Distance width = 1;
StrokeLineStyle style = 2;
Color color = 3;
}
enum GraphicFillType
{
GFT_UNKNOWN = 0;
GFT_UNFILLED = 1;
GFT_FILLED = 2;
}
message GraphicFillAttributes
{
GraphicFillType fill_type = 1;
// Color of the fill (not used in board and footprints)
Color color = 2;
}
message GraphicAttributes
{
StrokeAttributes stroke = 1;
GraphicFillAttributes fill = 2;
}

View File

@ -0,0 +1,119 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* enums.proto
* Includes protobuf versions of common KiCad enums
*/
syntax = "proto3";
package kiapi.common.types;
// The set of object types (from KICAD_T) that are exposed to the API.
enum KiCadObjectType
{
KOT_UNKNOWN = 0;
KOT_PCB_FOOTPRINT = 1;
KOT_PCB_PAD = 2;
KOT_PCB_SHAPE = 3;
KOT_PCB_REFERENCE_IMAGE = 4;
KOT_PCB_FIELD = 5;
KOT_PCB_GENERATOR = 6;
KOT_PCB_TEXT = 7;
KOT_PCB_TEXTBOX = 8;
KOT_PCB_TABLE = 9;
KOT_PCB_TABLECELL = 10;
KOT_PCB_TRACE = 11;
KOT_PCB_VIA = 12;
KOT_PCB_ARC = 13;
KOT_PCB_MARKER = 14;
KOT_PCB_DIMENSION = 15;
KOT_PCB_ZONE = 16;
KOT_PCB_GROUP = 17;
KOT_SCH_MARKER = 18;
KOT_SCH_JUNCTION = 19;
KOT_SCH_NO_CONNECT = 20;
KOT_SCH_BUS_WIRE_ENTRY = 21;
KOT_SCH_BUS_BUS_ENTRY = 22;
KOT_SCH_LINE = 23;
KOT_SCH_SHAPE = 24;
KOT_SCH_BITMAP = 25;
KOT_SCH_TEXTBOX = 26;
KOT_SCH_TEXT = 27;
KOT_SCH_TABLE = 28;
KOT_SCH_TABLECELL = 29;
KOT_SCH_LABEL = 30;
KOT_SCH_GLOBAL_LABEL = 31;
KOT_SCH_HIER_LABEL = 32;
KOT_SCH_DIRECTIVE_LABEL = 33;
KOT_SCH_FIELD = 34;
KOT_SCH_SYMBOL = 35;
KOT_SCH_SHEET_PIN = 36;
KOT_SCH_SHEET = 37;
KOT_SCH_PIN = 38;
KOT_LIB_SYMBOL = 39;
KOT_LIB_SHAPE = 40;
KOT_LIB_TEXT = 41;
KOT_LIB_TEXTBOX = 42;
KOT_LIB_PIN = 43;
KOT_LIB_FIELD = 44;
KOT_WSG_LINE = 45;
KOT_WSG_RECT = 46;
KOT_WSG_POLY = 47;
KOT_WSG_TEXT = 48;
KOT_WSG_BITMAP = 49;
KOT_WSG_PAGE = 50;
}
// Mapped to GR_TEXT_H_ALIGN_T
enum HorizontalAlignment
{
HA_UNKNOWN = 0;
HA_LEFT = 1;
HA_CENTER = 2;
HA_RIGHT = 3;
HA_INDETERMINATE = 4;
}
// Mapped to GR_TEXT_V_ALIGN_T
enum VerticalAlignment
{
VA_UNKNOWN = 0;
VA_TOP = 1;
VA_CENTER = 2;
VA_BOTTOM = 3;
VA_INDETERMINATE = 4;
}
// Mapped to LINE_STYLE
enum StrokeLineStyle
{
SLS_UNKNOWN = 0;
SLS_DEFAULT = 1;
SLS_SOLID = 2;
SLS_DASH = 3;
SLS_DOT = 4;
SLS_DASHDOT = 5;
SLS_DASHDOTDOT = 6;
}

View File

@ -0,0 +1,35 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* project_settings.proto
* Messages that describes project settings shared between schematics and boards
*/
syntax = "proto3";
package kiapi.common.project;
message NetClass
{
// The name of the netclass (the literal string "Default" for the default netclass)
string name = 1;
}

View File

@ -0,0 +1,24 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.schematic.types;

View File

@ -0,0 +1,75 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.schematic.types;
import "common/types/base_types.proto";
enum SchematicLayer
{
SL_UNKNOWN = 0;
}
/// Represents a schematic line segment, which may be a wire, bus, or graphical line
message Line
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 start = 2;
kiapi.common.types.Vector2 end = 3;
/**
* One of: LAYER_BUS, LAYER_WIRE, LAYER_NOTES
*/
SchematicLayer layer = 4;
}
message Text
{
kiapi.common.types.Text text = 1;
}
message LocalLabel
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 position = 2;
Text text = 3;
}
message GlobalLabel
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 position = 2;
Text text = 3;
}
message HierarchicalLabel
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 position = 2;
Text text = 3;
}
message DirectiveLabel
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Vector2 position = 2;
Text text = 3;
}

View File

@ -41,8 +41,7 @@ add_custom_target(
-DSRC_PATH=${PROJECT_SOURCE_DIR}
-DKICAD_CMAKE_MODULE_PATH=${KICAD_CMAKE_MODULE_PATH}
-P ${KICAD_CMAKE_MODULE_PATH}/BuildSteps/WriteVersionHeader.cmake
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
BYPRODUCTS ${CMAKE_BINARY_DIR}/kicad_build_version.h
WORKING_DIRECTORY ${CMAKE_BINARY_DIR} BYPRODUCTS ${CMAKE_BINARY_DIR}/kicad_build_version.h
DEPENDS ${KICAD_CMAKE_MODULE_PATH}/BuildSteps/WriteVersionHeader.cmake
COMMENT "Generating version string header"
)
@ -632,14 +631,14 @@ set( COMMON_SRCS
http_lib/http_lib_connection.cpp
http_lib/http_lib_settings.cpp
api/api_enums.cpp
api/api_utils.cpp
)
if( KICAD_IPC_API )
set( COMMON_SRCS
${COMMON_SRCS}
api/api_server.cpp
api/api_handler.cpp
api/api_handler_common.cpp
api/api_handler_editor.cpp
)
endif()
@ -693,6 +692,7 @@ target_include_directories( common
.
${CMAKE_BINARY_DIR}
$<TARGET_PROPERTY:argparse::argparse,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:kiapi,INTERFACE_INCLUDE_DIRECTORIES>
)
# text markup support
@ -794,6 +794,12 @@ set( PCB_COMMON_SRCS
widgets/net_selector.cpp
)
set( PCB_COMMON_SRCS
${PCB_COMMON_SRCS}
${CMAKE_SOURCE_DIR}/pcbnew/api/api_pcb_enums.cpp
${CMAKE_SOURCE_DIR}/pcbnew/api/api_pcb_utils.cpp
)
# add -DPCBNEW to compilation of these PCBNEW sources
set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES
COMPILE_DEFINITIONS "PCBNEW"

436
common/api/api_enums.cpp Normal file
View File

@ -0,0 +1,436 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <api/api_enums.h>
#include <import_export.h>
#include <api/common/types/enums.pb.h>
#include <api/board/board_types.pb.h>
#include <api/schematic/schematic_types.pb.h>
#include <core/typeinfo.h>
#include <font/text_attributes.h>
#include <layer_ids.h>
#include <stroke_params.h>
using namespace kiapi;
using namespace kiapi::common;
template<>
KICAD_T FromProtoEnum( types::KiCadObjectType aValue )
{
switch( aValue )
{
case types::KiCadObjectType::KOT_PCB_FOOTPRINT: return PCB_FOOTPRINT_T;
case types::KiCadObjectType::KOT_PCB_PAD: return PCB_PAD_T;
case types::KiCadObjectType::KOT_PCB_SHAPE: return PCB_SHAPE_T;
case types::KiCadObjectType::KOT_PCB_REFERENCE_IMAGE: return PCB_REFERENCE_IMAGE_T;
case types::KiCadObjectType::KOT_PCB_FIELD: return PCB_FIELD_T;
case types::KiCadObjectType::KOT_PCB_GENERATOR: return PCB_GENERATOR_T;
case types::KiCadObjectType::KOT_PCB_TEXT: return PCB_TEXT_T;
case types::KiCadObjectType::KOT_PCB_TEXTBOX: return PCB_TEXTBOX_T;
case types::KiCadObjectType::KOT_PCB_TABLE: return PCB_TABLE_T;
case types::KiCadObjectType::KOT_PCB_TABLECELL: return PCB_TABLECELL_T;
case types::KiCadObjectType::KOT_PCB_TRACE: return PCB_TRACE_T;
case types::KiCadObjectType::KOT_PCB_VIA: return PCB_VIA_T;
case types::KiCadObjectType::KOT_PCB_ARC: return PCB_ARC_T;
case types::KiCadObjectType::KOT_PCB_MARKER: return PCB_MARKER_T;
case types::KiCadObjectType::KOT_PCB_DIMENSION: return PCB_DIMENSION_T;
case types::KiCadObjectType::KOT_PCB_ZONE: return PCB_ZONE_T;
case types::KiCadObjectType::KOT_PCB_GROUP: return PCB_GROUP_T;
case types::KiCadObjectType::KOT_SCH_MARKER: return SCH_MARKER_T;
case types::KiCadObjectType::KOT_SCH_JUNCTION: return SCH_JUNCTION_T;
case types::KiCadObjectType::KOT_SCH_NO_CONNECT: return SCH_NO_CONNECT_T;
case types::KiCadObjectType::KOT_SCH_BUS_WIRE_ENTRY: return SCH_BUS_WIRE_ENTRY_T;
case types::KiCadObjectType::KOT_SCH_BUS_BUS_ENTRY: return SCH_BUS_BUS_ENTRY_T;
case types::KiCadObjectType::KOT_SCH_LINE: return SCH_LINE_T;
case types::KiCadObjectType::KOT_SCH_SHAPE: return SCH_SHAPE_T;
case types::KiCadObjectType::KOT_SCH_BITMAP: return SCH_BITMAP_T;
case types::KiCadObjectType::KOT_SCH_TEXTBOX: return SCH_TEXTBOX_T;
case types::KiCadObjectType::KOT_SCH_TEXT: return SCH_TEXT_T;
case types::KiCadObjectType::KOT_SCH_TABLE: return SCH_TABLE_T;
case types::KiCadObjectType::KOT_SCH_TABLECELL: return SCH_TABLECELL_T;
case types::KiCadObjectType::KOT_SCH_LABEL: return SCH_LABEL_T;
case types::KiCadObjectType::KOT_SCH_GLOBAL_LABEL: return SCH_GLOBAL_LABEL_T;
case types::KiCadObjectType::KOT_SCH_HIER_LABEL: return SCH_HIER_LABEL_T;
case types::KiCadObjectType::KOT_SCH_DIRECTIVE_LABEL: return SCH_DIRECTIVE_LABEL_T;
case types::KiCadObjectType::KOT_SCH_FIELD: return SCH_FIELD_T;
case types::KiCadObjectType::KOT_SCH_SYMBOL: return SCH_SYMBOL_T;
case types::KiCadObjectType::KOT_SCH_SHEET_PIN: return SCH_SHEET_PIN_T;
case types::KiCadObjectType::KOT_SCH_SHEET: return SCH_SHEET_T;
case types::KiCadObjectType::KOT_SCH_PIN: return SCH_PIN_T;
case types::KiCadObjectType::KOT_LIB_SYMBOL: return LIB_SYMBOL_T;
case types::KiCadObjectType::KOT_LIB_SHAPE: return LIB_SHAPE_T;
case types::KiCadObjectType::KOT_LIB_TEXT: return LIB_TEXT_T;
case types::KiCadObjectType::KOT_LIB_TEXTBOX: return LIB_TEXTBOX_T;
case types::KiCadObjectType::KOT_LIB_PIN: return LIB_PIN_T;
case types::KiCadObjectType::KOT_LIB_FIELD: return LIB_FIELD_T;
case types::KiCadObjectType::KOT_WSG_LINE: return WSG_LINE_T;
case types::KiCadObjectType::KOT_WSG_RECT: return WSG_RECT_T;
case types::KiCadObjectType::KOT_WSG_POLY: return WSG_POLY_T;
case types::KiCadObjectType::KOT_WSG_TEXT: return WSG_TEXT_T;
case types::KiCadObjectType::KOT_WSG_BITMAP: return WSG_BITMAP_T;
case types::KiCadObjectType::KOT_WSG_PAGE: return WSG_PAGE_T;
case types::KiCadObjectType::KOT_UNKNOWN: return TYPE_NOT_INIT;
default:
wxCHECK_MSG( false, TYPE_NOT_INIT,
"Unhandled case in FromProtoEnum<types::KiCadObjectType>" );
}
}
template<>
types::KiCadObjectType ToProtoEnum( KICAD_T aValue )
{
switch( aValue )
{
case PCB_FOOTPRINT_T: return types::KiCadObjectType::KOT_PCB_FOOTPRINT;
case PCB_PAD_T: return types::KiCadObjectType::KOT_PCB_PAD;
case PCB_SHAPE_T: return types::KiCadObjectType::KOT_PCB_SHAPE;
case PCB_REFERENCE_IMAGE_T: return types::KiCadObjectType::KOT_PCB_REFERENCE_IMAGE;
case PCB_FIELD_T: return types::KiCadObjectType::KOT_PCB_FIELD;
case PCB_GENERATOR_T: return types::KiCadObjectType::KOT_PCB_GENERATOR;
case PCB_TEXT_T: return types::KiCadObjectType::KOT_PCB_TEXT;
case PCB_TEXTBOX_T: return types::KiCadObjectType::KOT_PCB_TEXTBOX;
case PCB_TABLE_T: return types::KiCadObjectType::KOT_PCB_TABLE;
case PCB_TABLECELL_T: return types::KiCadObjectType::KOT_PCB_TABLECELL;
case PCB_TRACE_T: return types::KiCadObjectType::KOT_PCB_TRACE;
case PCB_VIA_T: return types::KiCadObjectType::KOT_PCB_VIA;
case PCB_ARC_T: return types::KiCadObjectType::KOT_PCB_ARC;
case PCB_MARKER_T: return types::KiCadObjectType::KOT_PCB_MARKER;
case PCB_DIMENSION_T: return types::KiCadObjectType::KOT_PCB_DIMENSION;
case PCB_ZONE_T: return types::KiCadObjectType::KOT_PCB_ZONE;
case PCB_GROUP_T: return types::KiCadObjectType::KOT_PCB_GROUP;
case SCH_MARKER_T: return types::KiCadObjectType::KOT_SCH_MARKER;
case SCH_JUNCTION_T: return types::KiCadObjectType::KOT_SCH_JUNCTION;
case SCH_NO_CONNECT_T: return types::KiCadObjectType::KOT_SCH_NO_CONNECT;
case SCH_BUS_WIRE_ENTRY_T: return types::KiCadObjectType::KOT_SCH_BUS_WIRE_ENTRY;
case SCH_BUS_BUS_ENTRY_T: return types::KiCadObjectType::KOT_SCH_BUS_BUS_ENTRY;
case SCH_LINE_T: return types::KiCadObjectType::KOT_SCH_LINE;
case SCH_SHAPE_T: return types::KiCadObjectType::KOT_SCH_SHAPE;
case SCH_BITMAP_T: return types::KiCadObjectType::KOT_SCH_BITMAP;
case SCH_TEXTBOX_T: return types::KiCadObjectType::KOT_SCH_TEXTBOX;
case SCH_TEXT_T: return types::KiCadObjectType::KOT_SCH_TEXT;
case SCH_TABLE_T: return types::KiCadObjectType::KOT_SCH_TABLE;
case SCH_TABLECELL_T: return types::KiCadObjectType::KOT_SCH_TABLECELL;
case SCH_LABEL_T: return types::KiCadObjectType::KOT_SCH_LABEL;
case SCH_GLOBAL_LABEL_T: return types::KiCadObjectType::KOT_SCH_GLOBAL_LABEL;
case SCH_HIER_LABEL_T: return types::KiCadObjectType::KOT_SCH_HIER_LABEL;
case SCH_DIRECTIVE_LABEL_T: return types::KiCadObjectType::KOT_SCH_DIRECTIVE_LABEL;
case SCH_FIELD_T: return types::KiCadObjectType::KOT_SCH_FIELD;
case SCH_SYMBOL_T: return types::KiCadObjectType::KOT_SCH_SYMBOL;
case SCH_SHEET_PIN_T: return types::KiCadObjectType::KOT_SCH_SHEET_PIN;
case SCH_SHEET_T: return types::KiCadObjectType::KOT_SCH_SHEET;
case SCH_PIN_T: return types::KiCadObjectType::KOT_SCH_PIN;
case LIB_SYMBOL_T: return types::KiCadObjectType::KOT_LIB_SYMBOL;
case LIB_SHAPE_T: return types::KiCadObjectType::KOT_LIB_SHAPE;
case LIB_TEXT_T: return types::KiCadObjectType::KOT_LIB_TEXT;
case LIB_TEXTBOX_T: return types::KiCadObjectType::KOT_LIB_TEXTBOX;
case LIB_PIN_T: return types::KiCadObjectType::KOT_LIB_PIN;
case LIB_FIELD_T: return types::KiCadObjectType::KOT_LIB_FIELD;
case WSG_LINE_T: return types::KiCadObjectType::KOT_WSG_LINE;
case WSG_RECT_T: return types::KiCadObjectType::KOT_WSG_RECT;
case WSG_POLY_T: return types::KiCadObjectType::KOT_WSG_POLY;
case WSG_TEXT_T: return types::KiCadObjectType::KOT_WSG_TEXT;
case WSG_BITMAP_T: return types::KiCadObjectType::KOT_WSG_BITMAP;
case WSG_PAGE_T: return types::KiCadObjectType::KOT_WSG_PAGE;
default:
wxCHECK_MSG( false, types::KiCadObjectType::KOT_UNKNOWN,
"Unhandled case in ToProtoEnum<KICAD_T>");
}
}
template<>
PCB_LAYER_ID FromProtoEnum( board::types::BoardLayer aValue )
{
switch( aValue )
{
case board::types::BoardLayer::BL_UNDEFINED: return UNDEFINED_LAYER;
case board::types::BoardLayer::BL_UNSELECTED: return UNSELECTED_LAYER;
case board::types::BoardLayer::BL_F_Cu: return F_Cu;
case board::types::BoardLayer::BL_In1_Cu: return In1_Cu;
case board::types::BoardLayer::BL_In2_Cu: return In2_Cu;
case board::types::BoardLayer::BL_In3_Cu: return In3_Cu;
case board::types::BoardLayer::BL_In4_Cu: return In4_Cu;
case board::types::BoardLayer::BL_In5_Cu: return In5_Cu;
case board::types::BoardLayer::BL_In6_Cu: return In6_Cu;
case board::types::BoardLayer::BL_In7_Cu: return In7_Cu;
case board::types::BoardLayer::BL_In8_Cu: return In8_Cu;
case board::types::BoardLayer::BL_In9_Cu: return In9_Cu;
case board::types::BoardLayer::BL_In10_Cu: return In10_Cu;
case board::types::BoardLayer::BL_In11_Cu: return In11_Cu;
case board::types::BoardLayer::BL_In12_Cu: return In12_Cu;
case board::types::BoardLayer::BL_In13_Cu: return In13_Cu;
case board::types::BoardLayer::BL_In14_Cu: return In14_Cu;
case board::types::BoardLayer::BL_In15_Cu: return In15_Cu;
case board::types::BoardLayer::BL_In16_Cu: return In16_Cu;
case board::types::BoardLayer::BL_In17_Cu: return In17_Cu;
case board::types::BoardLayer::BL_In18_Cu: return In18_Cu;
case board::types::BoardLayer::BL_In19_Cu: return In19_Cu;
case board::types::BoardLayer::BL_In20_Cu: return In20_Cu;
case board::types::BoardLayer::BL_In21_Cu: return In21_Cu;
case board::types::BoardLayer::BL_In22_Cu: return In22_Cu;
case board::types::BoardLayer::BL_In23_Cu: return In23_Cu;
case board::types::BoardLayer::BL_In24_Cu: return In24_Cu;
case board::types::BoardLayer::BL_In25_Cu: return In25_Cu;
case board::types::BoardLayer::BL_In26_Cu: return In26_Cu;
case board::types::BoardLayer::BL_In27_Cu: return In27_Cu;
case board::types::BoardLayer::BL_In28_Cu: return In28_Cu;
case board::types::BoardLayer::BL_In29_Cu: return In29_Cu;
case board::types::BoardLayer::BL_In30_Cu: return In30_Cu;
case board::types::BoardLayer::BL_B_Cu: return B_Cu;
case board::types::BoardLayer::BL_B_Adhes: return B_Adhes;
case board::types::BoardLayer::BL_F_Adhes: return F_Adhes;
case board::types::BoardLayer::BL_B_Paste: return B_Paste;
case board::types::BoardLayer::BL_F_Paste: return F_Paste;
case board::types::BoardLayer::BL_B_SilkS: return B_SilkS;
case board::types::BoardLayer::BL_F_SilkS: return F_SilkS;
case board::types::BoardLayer::BL_B_Mask: return B_Mask;
case board::types::BoardLayer::BL_F_Mask: return F_Mask;
case board::types::BoardLayer::BL_Dwgs_User: return Dwgs_User;
case board::types::BoardLayer::BL_Cmts_User: return Cmts_User;
case board::types::BoardLayer::BL_Eco1_User: return Eco1_User;
case board::types::BoardLayer::BL_Eco2_User: return Eco2_User;
case board::types::BoardLayer::BL_Edge_Cuts: return Edge_Cuts;
case board::types::BoardLayer::BL_Margin: return Margin;
case board::types::BoardLayer::BL_B_CrtYd: return B_CrtYd;
case board::types::BoardLayer::BL_F_CrtYd: return F_CrtYd;
case board::types::BoardLayer::BL_B_Fab: return B_Fab;
case board::types::BoardLayer::BL_F_Fab: return F_Fab;
case board::types::BoardLayer::BL_User_1: return User_1;
case board::types::BoardLayer::BL_User_2: return User_2;
case board::types::BoardLayer::BL_User_3: return User_3;
case board::types::BoardLayer::BL_User_4: return User_4;
case board::types::BoardLayer::BL_User_5: return User_5;
case board::types::BoardLayer::BL_User_6: return User_6;
case board::types::BoardLayer::BL_User_7: return User_7;
case board::types::BoardLayer::BL_User_8: return User_8;
case board::types::BoardLayer::BL_User_9: return User_9;
case board::types::BoardLayer::BL_UNKNOWN: return UNDEFINED_LAYER;
default:
wxCHECK_MSG( false, UNDEFINED_LAYER,
"Unhandled case in FromProtoEnum<board::types::BoardLayer>" );
}
}
template<>
board::types::BoardLayer ToProtoEnum( PCB_LAYER_ID aValue )
{
switch( aValue )
{
case UNDEFINED_LAYER: return board::types::BoardLayer::BL_UNDEFINED;
case UNSELECTED_LAYER: return board::types::BoardLayer::BL_UNSELECTED;
case F_Cu: return board::types::BoardLayer::BL_F_Cu;
case In1_Cu: return board::types::BoardLayer::BL_In1_Cu;
case In2_Cu: return board::types::BoardLayer::BL_In2_Cu;
case In3_Cu: return board::types::BoardLayer::BL_In3_Cu;
case In4_Cu: return board::types::BoardLayer::BL_In4_Cu;
case In5_Cu: return board::types::BoardLayer::BL_In5_Cu;
case In6_Cu: return board::types::BoardLayer::BL_In6_Cu;
case In7_Cu: return board::types::BoardLayer::BL_In7_Cu;
case In8_Cu: return board::types::BoardLayer::BL_In8_Cu;
case In9_Cu: return board::types::BoardLayer::BL_In9_Cu;
case In10_Cu: return board::types::BoardLayer::BL_In10_Cu;
case In11_Cu: return board::types::BoardLayer::BL_In11_Cu;
case In12_Cu: return board::types::BoardLayer::BL_In12_Cu;
case In13_Cu: return board::types::BoardLayer::BL_In13_Cu;
case In14_Cu: return board::types::BoardLayer::BL_In14_Cu;
case In15_Cu: return board::types::BoardLayer::BL_In15_Cu;
case In16_Cu: return board::types::BoardLayer::BL_In16_Cu;
case In17_Cu: return board::types::BoardLayer::BL_In17_Cu;
case In18_Cu: return board::types::BoardLayer::BL_In18_Cu;
case In19_Cu: return board::types::BoardLayer::BL_In19_Cu;
case In20_Cu: return board::types::BoardLayer::BL_In20_Cu;
case In21_Cu: return board::types::BoardLayer::BL_In21_Cu;
case In22_Cu: return board::types::BoardLayer::BL_In22_Cu;
case In23_Cu: return board::types::BoardLayer::BL_In23_Cu;
case In24_Cu: return board::types::BoardLayer::BL_In24_Cu;
case In25_Cu: return board::types::BoardLayer::BL_In25_Cu;
case In26_Cu: return board::types::BoardLayer::BL_In26_Cu;
case In27_Cu: return board::types::BoardLayer::BL_In27_Cu;
case In28_Cu: return board::types::BoardLayer::BL_In28_Cu;
case In29_Cu: return board::types::BoardLayer::BL_In29_Cu;
case In30_Cu: return board::types::BoardLayer::BL_In30_Cu;
case B_Cu: return board::types::BoardLayer::BL_B_Cu;
case B_Adhes: return board::types::BoardLayer::BL_B_Adhes;
case F_Adhes: return board::types::BoardLayer::BL_F_Adhes;
case B_Paste: return board::types::BoardLayer::BL_B_Paste;
case F_Paste: return board::types::BoardLayer::BL_F_Paste;
case B_SilkS: return board::types::BoardLayer::BL_B_SilkS;
case F_SilkS: return board::types::BoardLayer::BL_F_SilkS;
case B_Mask: return board::types::BoardLayer::BL_B_Mask;
case F_Mask: return board::types::BoardLayer::BL_F_Mask;
case Dwgs_User: return board::types::BoardLayer::BL_Dwgs_User;
case Cmts_User: return board::types::BoardLayer::BL_Cmts_User;
case Eco1_User: return board::types::BoardLayer::BL_Eco1_User;
case Eco2_User: return board::types::BoardLayer::BL_Eco2_User;
case Edge_Cuts: return board::types::BoardLayer::BL_Edge_Cuts;
case Margin: return board::types::BoardLayer::BL_Margin;
case B_CrtYd: return board::types::BoardLayer::BL_B_CrtYd;
case F_CrtYd: return board::types::BoardLayer::BL_F_CrtYd;
case B_Fab: return board::types::BoardLayer::BL_B_Fab;
case F_Fab: return board::types::BoardLayer::BL_F_Fab;
case User_1: return board::types::BoardLayer::BL_User_1;
case User_2: return board::types::BoardLayer::BL_User_2;
case User_3: return board::types::BoardLayer::BL_User_3;
case User_4: return board::types::BoardLayer::BL_User_4;
case User_5: return board::types::BoardLayer::BL_User_5;
case User_6: return board::types::BoardLayer::BL_User_6;
case User_7: return board::types::BoardLayer::BL_User_7;
case User_8: return board::types::BoardLayer::BL_User_8;
case User_9: return board::types::BoardLayer::BL_User_9;
default:
wxCHECK_MSG( false, board::types::BoardLayer::BL_UNKNOWN,
"Unhandled case in ToProtoEnum<PCB_LAYER_ID>");
}
}
template<>
SCH_LAYER_ID FromProtoEnum( schematic::types::SchematicLayer aValue )
{
switch( aValue )
{
default:
wxCHECK_MSG( false, SCH_LAYER_ID_START,
"Unhandled case in FromProtoEnum<schematic::types::SchematicLayer>" );
}
}
template<>
schematic::types::SchematicLayer ToProtoEnum( SCH_LAYER_ID aValue )
{
switch( aValue )
{
default:
wxCHECK_MSG( false, schematic::types::SchematicLayer::SL_UNKNOWN,
"Unhandled case in ToProtoEnum<SCH_LAYER_ID>");
}
}
template<>
GR_TEXT_H_ALIGN_T FromProtoEnum( types::HorizontalAlignment aValue )
{
switch( aValue )
{
case types::HorizontalAlignment::HA_LEFT: return GR_TEXT_H_ALIGN_LEFT;
case types::HorizontalAlignment::HA_CENTER: return GR_TEXT_H_ALIGN_CENTER;
case types::HorizontalAlignment::HA_RIGHT: return GR_TEXT_H_ALIGN_RIGHT;
case types::HorizontalAlignment::HA_INDETERMINATE: return GR_TEXT_H_ALIGN_INDETERMINATE;
case types::HorizontalAlignment::HA_UNKNOWN: return GR_TEXT_H_ALIGN_CENTER;
default:
wxCHECK_MSG( false, GR_TEXT_H_ALIGN_CENTER,
"Unhandled case in FromProtoEnum<types::HorizontalAlignment>" );
}
}
template<>
types::HorizontalAlignment ToProtoEnum( GR_TEXT_H_ALIGN_T aValue )
{
switch( aValue )
{
case GR_TEXT_H_ALIGN_LEFT: return types::HorizontalAlignment::HA_LEFT;
case GR_TEXT_H_ALIGN_CENTER: return types::HorizontalAlignment::HA_CENTER;
case GR_TEXT_H_ALIGN_RIGHT: return types::HorizontalAlignment::HA_RIGHT;
case GR_TEXT_H_ALIGN_INDETERMINATE: return types::HorizontalAlignment::HA_INDETERMINATE;
default:
wxCHECK_MSG( false, types::HorizontalAlignment::HA_UNKNOWN,
"Unhandled case in ToProtoEnum<GR_TEXT_H_ALIGN_T>");
}
}
template<>
GR_TEXT_V_ALIGN_T FromProtoEnum( types::VerticalAlignment aValue )
{
switch( aValue )
{
case types::VerticalAlignment::VA_TOP: return GR_TEXT_V_ALIGN_TOP;
case types::VerticalAlignment::VA_CENTER: return GR_TEXT_V_ALIGN_CENTER;
case types::VerticalAlignment::VA_BOTTOM: return GR_TEXT_V_ALIGN_BOTTOM;
case types::VerticalAlignment::VA_INDETERMINATE: return GR_TEXT_V_ALIGN_INDETERMINATE;
case types::VerticalAlignment::VA_UNKNOWN: return GR_TEXT_V_ALIGN_CENTER;
default:
wxCHECK_MSG( false, GR_TEXT_V_ALIGN_CENTER,
"Unhandled case in FromProtoEnum<types::VerticalAlignment>" );
}
}
template<>
types::VerticalAlignment ToProtoEnum( GR_TEXT_V_ALIGN_T aValue )
{
switch( aValue )
{
case GR_TEXT_V_ALIGN_TOP: return types::VerticalAlignment::VA_TOP;
case GR_TEXT_V_ALIGN_CENTER: return types::VerticalAlignment::VA_CENTER;
case GR_TEXT_V_ALIGN_BOTTOM: return types::VerticalAlignment::VA_BOTTOM;
case GR_TEXT_V_ALIGN_INDETERMINATE: return types::VerticalAlignment::VA_INDETERMINATE;
default:
wxCHECK_MSG( false, types::VerticalAlignment::VA_UNKNOWN,
"Unhandled case in ToProtoEnum<GR_TEXT_V_ALIGN_T>");
}
}
template<>
LINE_STYLE FromProtoEnum( types::StrokeLineStyle aValue )
{
switch( aValue )
{
case types::StrokeLineStyle::SLS_DEFAULT: return LINE_STYLE::DEFAULT;
case types::StrokeLineStyle::SLS_SOLID: return LINE_STYLE::SOLID;
case types::StrokeLineStyle::SLS_DASH: return LINE_STYLE::DASH;
case types::StrokeLineStyle::SLS_DOT: return LINE_STYLE::DOT;
case types::StrokeLineStyle::SLS_DASHDOT: return LINE_STYLE::DASHDOT;
case types::StrokeLineStyle::SLS_DASHDOTDOT: return LINE_STYLE::DASHDOTDOT;
case types::StrokeLineStyle::SLS_UNKNOWN:
default:
wxCHECK_MSG( false, LINE_STYLE::DEFAULT,
"Unhandled case in FromProtoEnum<types::StrokeLineStyle>" );
}
}
template<>
types::StrokeLineStyle ToProtoEnum( LINE_STYLE aValue )
{
switch( aValue )
{
case LINE_STYLE::DEFAULT: return types::StrokeLineStyle::SLS_DEFAULT;
case LINE_STYLE::SOLID: return types::StrokeLineStyle::SLS_SOLID;
case LINE_STYLE::DASH: return types::StrokeLineStyle::SLS_DASH;
case LINE_STYLE::DOT: return types::StrokeLineStyle::SLS_DOT;
case LINE_STYLE::DASHDOT: return types::StrokeLineStyle::SLS_DASHDOT;
case LINE_STYLE::DASHDOTDOT: return types::StrokeLineStyle::SLS_DASHDOTDOT;
default:
wxCHECK_MSG( false, types::StrokeLineStyle::SLS_UNKNOWN,
"Unhandled case in ToProtoEnum<LINE_STYLE>");
}
}

View File

@ -19,10 +19,14 @@
*/
#include <api/api_handler.h>
#include <wx/wx.h>
using kiapi::common::ApiRequest, kiapi::common::ApiResponse, kiapi::common::ApiResponseStatus;
const wxString API_HANDLER::m_defaultCommitMessage = _( "Modification from API" );
API_RESULT API_HANDLER::Handle( ApiRequest& aMsg )
{
ApiResponseStatus status;
@ -55,20 +59,3 @@ API_RESULT API_HANDLER::Handle( ApiRequest& aMsg )
// This response is used internally; no need for an error message
return tl::unexpected( status );
}
std::optional<KICAD_T> API_HANDLER::TypeNameFromAny( const google::protobuf::Any& aMessage )
{
static const std::map<std::string, KICAD_T> s_types = {
{ "type.googleapis.com/kiapi.board.types.Track", PCB_TRACE_T },
{ "type.googleapis.com/kiapi.board.types.Arc", PCB_ARC_T },
{ "type.googleapis.com/kiapi.board.types.Via", PCB_VIA_T },
};
auto it = s_types.find( aMessage.type_url() );
if( it != s_types.end() )
return it->second;
return std::nullopt;
}

View File

@ -22,7 +22,11 @@
#include <api/api_handler_common.h>
#include <build_version.h>
#include <google/protobuf/empty.pb.h>
#include <pgm_base.h>
#include <project/net_settings.h>
#include <project/project_file.h>
#include <settings/settings_manager.h>
#include <wx/string.h>
using namespace kiapi::common::commands;
@ -34,10 +38,13 @@ API_HANDLER_COMMON::API_HANDLER_COMMON() :
API_HANDLER()
{
registerHandler<GetVersion, GetVersionResponse>( &API_HANDLER_COMMON::handleGetVersion );
registerHandler<GetNetClasses, NetClassesResponse>( &API_HANDLER_COMMON::handleGetNetClasses );
registerHandler<Ping, Empty>( &API_HANDLER_COMMON::handlePing );
}
HANDLER_RESULT<GetVersionResponse> API_HANDLER_COMMON::handleGetVersion( GetVersion& aMsg )
HANDLER_RESULT<GetVersionResponse> API_HANDLER_COMMON::handleGetVersion( GetVersion&,
const HANDLER_CONTEXT& )
{
GetVersionResponse reply;
@ -50,3 +57,26 @@ HANDLER_RESULT<GetVersionResponse> API_HANDLER_COMMON::handleGetVersion( GetVers
return reply;
}
HANDLER_RESULT<NetClassesResponse> API_HANDLER_COMMON::handleGetNetClasses( GetNetClasses& aMsg,
const HANDLER_CONTEXT& aCtx )
{
NetClassesResponse reply;
std::shared_ptr<NET_SETTINGS>& netSettings =
Pgm().GetSettingsManager().Prj().GetProjectFile().m_NetSettings;
for( const auto& [name, netClass] : netSettings->m_NetClasses )
{
reply.add_net_classes()->set_name( name.ToStdString() );
}
return reply;
}
HANDLER_RESULT<Empty> API_HANDLER_COMMON::handlePing( Ping& aMsg, const HANDLER_CONTEXT& aCtx )
{
return Empty();
}

View File

@ -0,0 +1,353 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <api/api_handler_editor.h>
#include <api/api_utils.h>
#include <eda_base_frame.h>
#include <eda_item.h>
#include <wx/wx.h>
using namespace kiapi::common::commands;
API_HANDLER_EDITOR::API_HANDLER_EDITOR( EDA_BASE_FRAME* aFrame ) :
API_HANDLER(),
m_frame( aFrame )
{
registerHandler<BeginCommit, BeginCommitResponse>( &API_HANDLER_EDITOR::handleBeginCommit );
registerHandler<EndCommit, EndCommitResponse>( &API_HANDLER_EDITOR::handleEndCommit );
registerHandler<CreateItems, CreateItemsResponse>( &API_HANDLER_EDITOR::handleCreateItems );
registerHandler<UpdateItems, UpdateItemsResponse>( &API_HANDLER_EDITOR::handleUpdateItems );
registerHandler<DeleteItems, DeleteItemsResponse>( &API_HANDLER_EDITOR::handleDeleteItems );
registerHandler<HitTest, HitTestResponse>( &API_HANDLER_EDITOR::handleHitTest );
}
HANDLER_RESULT<BeginCommitResponse> API_HANDLER_EDITOR::handleBeginCommit( BeginCommit& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
if( m_commits.count( aCtx.ClientName ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "the client {} already has a commit in progress",
aCtx.ClientName ) );
return tl::unexpected( e );
}
wxASSERT( !m_activeClients.count( aCtx.ClientName ) );
BeginCommitResponse response;
KIID id;
m_commits[aCtx.ClientName] = std::make_pair( id, createCommit() );
response.mutable_id()->set_value( id.AsStdString() );
m_activeClients.insert( aCtx.ClientName );
return response;
}
HANDLER_RESULT<EndCommitResponse> API_HANDLER_EDITOR::handleEndCommit( EndCommit& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
if( !m_commits.count( aCtx.ClientName ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "the client {} does not has a commit in progress",
aCtx.ClientName ) );
return tl::unexpected( e );
}
wxASSERT( m_activeClients.count( aCtx.ClientName ) );
const std::pair<KIID, std::unique_ptr<COMMIT>>& pair = m_commits.at( aCtx.ClientName );
const KIID& id = pair.first;
const std::unique_ptr<COMMIT>& commit = pair.second;
EndCommitResponse response;
// Do not check IDs with drop; it is a safety net in case the id was lost on the client side
switch( aMsg.action() )
{
case kiapi::common::commands::CMA_DROP:
{
commit->Revert();
m_commits.erase( aCtx.ClientName );
m_activeClients.erase( aCtx.ClientName );
break;
}
case kiapi::common::commands::CMA_COMMIT:
{
if( aMsg.id().value().compare( id.AsStdString() ) != 0 )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "the id {} does not match the commit in progress",
aMsg.id().value() ) );
return tl::unexpected( e );
}
pushCurrentCommit( aCtx, wxString( aMsg.message().c_str(), wxConvUTF8 ) );
break;
}
default:
break;
}
return response;
}
COMMIT* API_HANDLER_EDITOR::getCurrentCommit( const HANDLER_CONTEXT& aCtx )
{
if( !m_commits.count( aCtx.ClientName ) )
{
KIID id;
m_commits[aCtx.ClientName] = std::make_pair( id, createCommit() );
}
return m_commits.at( aCtx.ClientName ).second.get();
}
void API_HANDLER_EDITOR::pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage )
{
auto it = m_commits.find( aCtx.ClientName );
if( it == m_commits.end() )
return;
it->second.second->Push( aMessage.IsEmpty() ? m_defaultCommitMessage : aMessage );
m_commits.erase( it );
m_activeClients.erase( aCtx.ClientName );
}
HANDLER_RESULT<bool> API_HANDLER_EDITOR::validateDocument(
const kiapi::common::types::DocumentSpecifier& aDocument )
{
if( !validateDocumentInternal( aDocument ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "the requested document {} is not open",
aDocument.board_filename() ) );
return tl::unexpected( e );
}
return true;
}
HANDLER_RESULT<std::optional<KIID>> API_HANDLER_EDITOR::validateItemHeaderDocument(
const kiapi::common::types::ItemHeader& aHeader )
{
if( !aHeader.has_document() || aHeader.document().type() != thisDocumentType() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_UNHANDLED );
// No error message, this is a flag that the server should try a different handler
return tl::unexpected( e );
}
HANDLER_RESULT<bool> documentValidation = validateDocument( aHeader.document() );
if( !documentValidation )
return tl::unexpected( documentValidation.error() );
if( !validateDocumentInternal( aHeader.document() ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "the requested document {} is not open",
aHeader.document().board_filename() ) );
return tl::unexpected( e );
}
if( aHeader.has_container() )
{
return KIID( aHeader.container().value() );
}
// Valid header, but no container provided
return std::nullopt;
}
std::optional<ApiResponseStatus> API_HANDLER_EDITOR::checkForBusy()
{
if( !m_frame->CanAcceptApiCommands() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BUSY );
e.set_error_message( "KiCad is busy and cannot respond to API requests right now" );
return e;
}
return std::nullopt;
}
HANDLER_RESULT<CreateItemsResponse> API_HANDLER_EDITOR::handleCreateItems( CreateItems& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
CreateItemsResponse response;
HANDLER_RESULT<ItemRequestStatus> result = handleCreateUpdateItemsInternal( true, aCtx,
aMsg.header(), aMsg.items(),
[&]( const ItemStatus& aStatus, const google::protobuf::Any& aItem )
{
ItemCreationResult itemResult;
itemResult.mutable_status()->CopyFrom( aStatus );
itemResult.mutable_item()->CopyFrom( aItem );
response.mutable_created_items()->Add( std::move( itemResult ) );
} );
if( !result.has_value() )
return tl::unexpected( result.error() );
response.set_status( *result );
return response;
}
HANDLER_RESULT<UpdateItemsResponse> API_HANDLER_EDITOR::handleUpdateItems( UpdateItems& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
UpdateItemsResponse response;
HANDLER_RESULT<ItemRequestStatus> result = handleCreateUpdateItemsInternal( false, aCtx,
aMsg.header(), aMsg.items(),
[&]( const ItemStatus& aStatus, const google::protobuf::Any& aItem )
{
ItemUpdateResult itemResult;
itemResult.mutable_status()->CopyFrom( aStatus );
itemResult.mutable_item()->CopyFrom( aItem );
response.mutable_updated_items()->Add( std::move( itemResult ) );
} );
if( !result.has_value() )
return tl::unexpected( result.error() );
response.set_status( *result );
return response;
}
HANDLER_RESULT<DeleteItemsResponse> API_HANDLER_EDITOR::handleDeleteItems( DeleteItems& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
if( !validateItemHeaderDocument( aMsg.header() ) )
{
ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
std::map<KIID, ItemDeletionStatus> itemsToDelete;
for( const kiapi::common::types::KIID& kiidBuf : aMsg.item_ids() )
{
if( !kiidBuf.value().empty() )
{
KIID kiid( kiidBuf.value() );
itemsToDelete[kiid] = ItemDeletionStatus::IDS_NONEXISTENT;
}
}
if( itemsToDelete.empty() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "no valid items to delete were given" );
return tl::unexpected( e );
}
deleteItemsInternal( itemsToDelete, aCtx );
DeleteItemsResponse response;
for( const auto& [id, status] : itemsToDelete )
{
ItemDeletionResult result;
result.mutable_id()->set_value( id.AsStdString() );
result.set_status( status );
}
response.set_status( kiapi::common::types::ItemRequestStatus::IRS_OK );
return response;
}
HANDLER_RESULT<HitTestResponse> API_HANDLER_EDITOR::handleHitTest( HitTest& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
if( !validateItemHeaderDocument( aMsg.header() ) )
{
ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
HitTestResponse response;
std::optional<EDA_ITEM*> item = getItemFromDocument( aMsg.header().document(),
KIID( aMsg.id().value() ) );
if( !item )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "the requested item ID is not present in the given document" );
return tl::unexpected( e );
}
if( ( *item )->HitTest( kiapi::common::UnpackVector2( aMsg.position() ), aMsg.tolerance() ) )
response.set_result( HitTestResult::HTR_HIT );
else
response.set_result( HitTestResult::HTR_NO_HIT );
return response;
}

View File

@ -312,7 +312,8 @@ std::optional<PLUGIN_ACTION> API_PLUGIN::createActionFromJson( const nlohmann::j
}
wxBitmap bmp;
bmp.LoadFile( iconFile.GetFullPath() );
// TODO: If necessary; support types other than PNG
bmp.LoadFile( iconFile.GetFullPath(), wxBITMAP_TYPE_PNG );
if( bmp.IsOk() )
bitmaps.push_back( bmp );

View File

@ -303,10 +303,15 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent )
return;
}
wxLogTrace( traceApi, wxString::Format( "Manager: begin processing; %zu jobs left in queue",
m_jobs.size() ) );
JOB& job = m_jobs.front();
if( job.type == JOB_TYPE::CREATE_ENV )
{
wxLogTrace( traceApi, "Manager: Python exe '%s'",
Pgm().GetCommonSettings()->m_Api.python_interpreter );
wxLogTrace( traceApi, wxString::Format( "Manager: creating Python env at %s",
job.env_path ) );
PYTHON_MANAGER manager( Pgm().GetCommonSettings()->m_Api.python_interpreter );
@ -330,6 +335,7 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent )
wxLogTrace( traceApi, wxString::Format( "Manager: installing dependencies for %s",
job.plugin_path ) );
std::optional<wxString> pythonHome = PYTHON_MANAGER::GetPythonEnvironment( job.identifier );
std::optional<wxString> python = PYTHON_MANAGER::GetVirtualPython( job.identifier );
wxFileName reqs = wxFileName( job.plugin_path, "requirements.txt" );
@ -337,28 +343,46 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent )
{
wxLogTrace( traceApi, wxString::Format( "Manager: error: python not found at %s",
job.env_path ) );
wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED,
wxID_ANY );
QueueEvent( evt );
}
else if( !reqs.IsFileReadable() )
{
wxLogTrace( traceApi,
wxString::Format( "Manager: error: requirements.txt not found at %s",
job.plugin_path ) );
wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED,
wxID_ANY );
QueueEvent( evt );
}
else
{
PYTHON_MANAGER manager( *python );
wxLogTrace( traceApi, "Manager: Python exe '%s'", *python );
wxString cmd = wxString::Format(
PYTHON_MANAGER manager( *python );
wxExecuteEnv env;
if( pythonHome )
env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome;
wxString cmd = wxS( "-m ensurepip" );
wxLogTrace( traceApi, "Manager: calling python `%s`", cmd );
manager.Execute( cmd,
[=]( int aRetVal, const wxString& aOutput, const wxString& aError )
{
wxLogTrace( traceApi, wxString::Format( "Manager: ensurepip (%d): %s",
aRetVal, aOutput ) );
if( !aError.IsEmpty() )
{
wxLogTrace( traceApi,
wxString::Format( "Manager: ensurepip err: %s", aError ) );
}
}, &env );
cmd = wxString::Format(
wxS( "-m pip install --no-input --isolated --require-virtualenv "
"--exists-action i -r '%s'" ),
reqs.GetFullPath() );
wxLogTrace( traceApi, "Manager: calling python `%s`", cmd );
manager.Execute( cmd,
[=]( int aRetVal, const wxString& aOutput, const wxString& aError )
{
@ -382,10 +406,14 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent )
wxID_ANY );
QueueEvent( evt );
} );
}, &env );
}
wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
QueueEvent( evt );
}
m_jobs.pop_front();
wxLogTrace( traceApi, wxString::Format( "Manager: %zu jobs left in queue", m_jobs.size() ) );
wxLogTrace( traceApi, wxString::Format( "Manager: done processing; %zu jobs left in queue",
m_jobs.size() ) );
}

View File

@ -95,9 +95,11 @@ KICAD_API_SERVER::KICAD_API_SERVER() :
m_logFilePath.SetName( s_logFileName );
if( ADVANCED_CFG::GetCfg().m_EnableAPILogging )
{
PATHS::EnsurePathExists( PATHS::GetLogsPath() );
log( fmt::format( "--- KiCad API server started at {} ---\n", SocketPath() ) );
}
log( fmt::format( "--- KiCad API server started at {} ---\n", SocketPath() ) );
wxLogTrace( traceApi, wxString::Format( "Server: listening at %s", SocketPath() ) );
Bind( API_REQUEST_EVENT, &KICAD_API_SERVER::handleApiEvent, this );
@ -168,10 +170,13 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent )
error.mutable_status()->set_status( ApiStatusCode::AS_BAD_REQUEST );
error.mutable_status()->set_error_message( "request could not be parsed" );
m_server->Reply( error.SerializeAsString() );
log( "Response (ERROR): " + error.Utf8DebugString() );
if( ADVANCED_CFG::GetCfg().m_EnableAPILogging )
log( "Response (ERROR): " + error.Utf8DebugString() );
}
log( "Request: " + request.Utf8DebugString() );
if( ADVANCED_CFG::GetCfg().m_EnableAPILogging )
log( "Request: " + request.Utf8DebugString() );
if( !request.header().kicad_token().empty() &&
request.header().kicad_token().compare( m_token ) != 0 )
@ -182,7 +187,9 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent )
error.mutable_status()->set_error_message(
"the provided kicad_token did not match this KiCad instance's token" );
m_server->Reply( error.SerializeAsString() );
log( "Response (ERROR): " + error.Utf8DebugString() );
if( ADVANCED_CFG::GetCfg().m_EnableAPILogging )
log( "Response (ERROR): " + error.Utf8DebugString() );
}
API_RESULT result;
@ -203,7 +210,9 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent )
{
result->mutable_header()->set_kicad_token( m_token );
m_server->Reply( result->SerializeAsString() );
log( "Response: " + result->Utf8DebugString() );
if( ADVANCED_CFG::GetCfg().m_EnableAPILogging )
log( "Response: " + result->Utf8DebugString() );
}
else
{
@ -220,16 +229,15 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent )
}
m_server->Reply( error.SerializeAsString() );
log( "Response (ERROR): " + error.Utf8DebugString() );
if( ADVANCED_CFG::GetCfg().m_EnableAPILogging )
log( "Response (ERROR): " + error.Utf8DebugString() );
}
}
void KICAD_API_SERVER::log( const std::string& aOutput )
{
if( !ADVANCED_CFG::GetCfg().m_EnableAPILogging )
return;
FILE* fp = wxFopen( m_logFilePath.GetFullPath(), wxT( "a" ) );
if( !fp )

80
common/api/api_utils.cpp Normal file
View File

@ -0,0 +1,80 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <magic_enum.hpp>
#include <api/api_utils.h>
namespace kiapi::common
{
std::optional<KICAD_T> TypeNameFromAny( const google::protobuf::Any& aMessage )
{
static const std::map<std::string, KICAD_T> s_types = {
{ "type.googleapis.com/kiapi.board.types.Track", PCB_TRACE_T },
{ "type.googleapis.com/kiapi.board.types.Arc", PCB_ARC_T },
{ "type.googleapis.com/kiapi.board.types.Via", PCB_VIA_T },
{ "type.googleapis.com/kiapi.board.types.Text", PCB_TEXT_T },
{ "type.googleapis.com/kiapi.board.types.TextBox", PCB_TEXTBOX_T },
{ "type.googleapis.com/kiapi.board.types.GraphicShape", PCB_SHAPE_T },
{ "type.googleapis.com/kiapi.board.types.Pad", PCB_PAD_T },
{ "type.googleapis.com/kiapi.board.types.Zone", PCB_ZONE_T },
{ "type.googleapis.com/kiapi.board.types.Dimension", PCB_DIMENSION_T },
{ "type.googleapis.com/kiapi.board.types.ReferenceImage", PCB_REFERENCE_IMAGE_T },
{ "type.googleapis.com/kiapi.board.types.Group", PCB_GROUP_T },
{ "type.googleapis.com/kiapi.board.types.Field", PCB_FIELD_T },
{ "type.googleapis.com/kiapi.board.types.FootprintInstance", PCB_FOOTPRINT_T },
};
auto it = s_types.find( aMessage.type_url() );
if( it != s_types.end() )
return it->second;
return std::nullopt;
}
LIB_ID LibIdFromProto( const types::LibraryIdentifier& aId )
{
return LIB_ID( aId.library_nickname(), aId.entry_name() );
}
types::LibraryIdentifier LibIdToProto( const LIB_ID& aId )
{
types::LibraryIdentifier msg;
msg.set_library_nickname( aId.GetLibNickname() );
msg.set_entry_name( aId.GetLibItemName() );
return msg;
}
void PackVector2( kiapi::common::types::Vector2& aOutput, const VECTOR2I aInput )
{
aOutput.set_x_nm( aInput.x );
aOutput.set_y_nm( aInput.y );
}
VECTOR2I UnpackVector2( const types::Vector2& aInput )
{
return VECTOR2I( aInput.x_nm(), aInput.y_nm() );
}
} // namespace kiapi::common

View File

@ -97,6 +97,7 @@ void PANEL_PLUGIN_SETTINGS::OnEnableApiChecked( wxCommandEvent& aEvent )
void PANEL_PLUGIN_SETTINGS::updateApiStatusText()
{
#ifdef KICAD_IPC_API
if( m_cbEnableApi->GetValue() && Pgm().GetApiServer().Running() )
{
m_stApiStatus->SetLabel( wxString::Format( _( "Listening at %s" ),
@ -106,6 +107,9 @@ void PANEL_PLUGIN_SETTINGS::updateApiStatusText()
{
m_stApiStatus->SetLabel( wxEmptyString );
}
#else
m_stApiStatus->SetLabel( _( "This installation of KiCad does not have API support enabled." ) );
#endif
}

View File

@ -23,6 +23,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <api/api_plugin_manager.h>
#include <base_screen.h>
#include <bitmaps.h>
#include <confirm.h>
@ -35,6 +36,7 @@
#include <gal/graphics_abstraction_layer.h>
#include <id.h>
#include <kiface_base.h>
#include <kiplatform/ui.h>
#include <lockfile.h>
#include <macros.h>
#include <math/vector2wx.h>
@ -1343,3 +1345,45 @@ bool EDA_DRAW_FRAME::SaveCanvasImageToFile( const wxString& aFileName,
image.Destroy();
return retv;
}
void EDA_DRAW_FRAME::addApiPluginTools()
{
#ifdef KICAD_IPC_API
// TODO: Add user control over visibility and order
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
mgr.ButtonBindings().clear();
std::vector<const PLUGIN_ACTION*> actions = mgr.GetActionsForScope( PluginActionScope() );
for( auto& action : actions )
{
if( !action->show_button )
continue;
const wxBitmapBundle& icon = KIPLATFORM::UI::IsDarkTheme() && action->icon_dark.IsOk()
? action->icon_dark
: action->icon_light;
wxAuiToolBarItem* button = m_mainToolBar->AddTool( wxID_ANY, wxEmptyString, icon,
action->name );
Connect( button->GetId(), wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler( EDA_DRAW_FRAME::OnApiPluginInvoke ) );
mgr.ButtonBindings().insert( { button->GetId(), action->identifier } );
}
#endif
}
void EDA_DRAW_FRAME::OnApiPluginInvoke( wxCommandEvent& aEvent )
{
#ifdef KICAD_IPC_API
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
if( mgr.ButtonBindings().count( aEvent.GetId() ) )
mgr.InvokeAction( mgr.ButtonBindings().at( aEvent.GetId() ) );
#endif
}

View File

@ -94,6 +94,7 @@ wxString EDA_SHAPE::SHAPE_T_asString() const
case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" );
case SHAPE_T::POLY: return wxS( "S_POLYGON" );
case SHAPE_T::BEZIER: return wxS( "S_CURVE" );
case SHAPE_T::UNDEFINED: return wxS( "UNDEFINED" );
}
return wxEmptyString; // Just to quiet GCC.
@ -243,12 +244,14 @@ void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
switch ( m_shape )
{
case SHAPE_T::ARC:
m_arcCenter += aMoveVector;
KI_FALLTHROUGH;
case SHAPE_T::SEGMENT:
case SHAPE_T::RECTANGLE:
case SHAPE_T::CIRCLE:
m_start += aMoveVector;
m_end += aMoveVector;
m_arcCenter += aMoveVector;
break;
case SHAPE_T::POLY:
@ -284,11 +287,13 @@ void EDA_SHAPE::scale( double aScale )
switch( m_shape )
{
case SHAPE_T::ARC:
scalePt( m_arcCenter );
KI_FALLTHROUGH;
case SHAPE_T::SEGMENT:
case SHAPE_T::RECTANGLE:
scalePt( m_start );
scalePt( m_end );
scalePt( m_arcCenter );
break;
case SHAPE_T::CIRCLE: // ring or circle

View File

@ -558,7 +558,10 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest )
m_settings_manager = std::make_unique<SETTINGS_MANAGER>( aHeadless );
m_background_jobs_monitor = std::make_unique<BACKGROUND_JOBS_MONITOR>();
m_notifications_manager = std::make_unique<NOTIFICATIONS_MANAGER>();
#ifdef KICAD_IPC_API
m_plugin_manager = std::make_unique<API_PLUGIN_MANAGER>( &App() );
#endif
// Our unit test mocks break if we continue
// A bug caused InitPgm to terminate early in unit tests and the mocks are...simplistic
@ -613,7 +616,9 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest )
// Need to create a project early for now (it can have an empty path for the moment)
GetSettingsManager().LoadProject( "" );
#ifdef KICAD_IPC_API
m_plugin_manager->ReloadPlugins();
#endif
// This sets the maximum tooltip display duration to 10s (up from 5) but only affects
// Windows as other platforms display tooltips while the mouse is not moving

View File

@ -1143,6 +1143,15 @@ TOOL_ACTION ACTIONS::ddAddLibrary( TOOL_ACTION_ARGS()
.Name( "common.Control.ddaddLibrary" )
.Scope( AS_GLOBAL ) );
// API
TOOL_ACTION ACTIONS::pluginsReload( TOOL_ACTION_ARGS()
.Name( "common.API.pluginsReload" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Refresh Plugins" ) )
.Tooltip( _( "Reload all python plugins and refresh plugin menus" ) )
.Icon( BITMAPS::reload ) );
// System-wide selection Events
const TOOL_EVENT EVENTS::PointSelectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.pointSelected" );

View File

@ -422,6 +422,8 @@ set( EESCHEMA_SRCS
toolbars_sch_editor.cpp
toolbars_symbol_viewer.cpp
api/api_sch_utils.cpp
netlist_exporters/netlist_exporter_allegro.cpp
netlist_exporters/netlist_exporter_base.cpp
netlist_exporters/netlist_exporter_cadstar.cpp
@ -457,6 +459,13 @@ set( EESCHEMA_SRCS
tools/symbol_editor_pin_tool.cpp
)
if( KICAD_IPC_API )
set( EESCHEMA_SRCS
${EESCHEMA_SRCS}
api/api_handler_sch.cpp
)
endif()
if( WIN32 )
if( MINGW )
# EESCHEMA_RESOURCES variable is set by the macro.

View File

@ -0,0 +1,305 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <api/api_handler_sch.h>
#include <api/api_sch_utils.h>
#include <api/api_utils.h>
#include <magic_enum.hpp>
#include <sch_commit.h>
#include <sch_edit_frame.h>
#include <wx/filename.h>
#include <api/common/types/base_types.pb.h>
using namespace kiapi::common::commands;
using kiapi::common::types::CommandStatus;
using kiapi::common::types::DocumentType;
using kiapi::common::types::ItemRequestStatus;
API_HANDLER_SCH::API_HANDLER_SCH( SCH_EDIT_FRAME* aFrame ) :
API_HANDLER_EDITOR(),
m_frame( aFrame )
{
registerHandler<GetOpenDocuments, GetOpenDocumentsResponse>(
&API_HANDLER_SCH::handleGetOpenDocuments );
}
std::unique_ptr<COMMIT> API_HANDLER_SCH::createCommit()
{
return std::make_unique<SCH_COMMIT>( m_frame );
}
bool API_HANDLER_SCH::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
{
if( aDocument.type() != DocumentType::DOCTYPE_SCHEMATIC )
return false;
// TODO(JE) need serdes for SCH_SHEET_PATH <> SheetPath
return true;
//wxString currentPath = m_frame->GetCurrentSheet().PathAsString();
//return 0 == aDocument.sheet_path().compare( currentPath.ToStdString() );
}
HANDLER_RESULT<GetOpenDocumentsResponse> API_HANDLER_SCH::handleGetOpenDocuments(
GetOpenDocuments& aMsg, const HANDLER_CONTEXT& )
{
if( aMsg.type() != DocumentType::DOCTYPE_SCHEMATIC )
{
ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
GetOpenDocumentsResponse response;
common::types::DocumentSpecifier doc;
wxFileName fn( m_frame->GetCurrentFileName() );
doc.set_type( DocumentType::DOCTYPE_SCHEMATIC );
doc.set_board_filename( fn.GetFullName() );
response.mutable_documents()->Add( std::move( doc ) );
return response;
}
HANDLER_RESULT<std::unique_ptr<EDA_ITEM>> API_HANDLER_SCH::createItemForType( KICAD_T aType,
EDA_ITEM* aContainer )
{
if( !aContainer )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "Tried to create an item in a null container" );
return tl::unexpected( e );
}
if( aType == SCH_PIN_T && !dynamic_cast<SCH_SYMBOL*>( aContainer ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "Tried to create a pin in {}, which is not a symbol",
aContainer->GetFriendlyName().ToStdString() ) );
return tl::unexpected( e );
}
else if( aType == SCH_SYMBOL_T && !dynamic_cast<SCHEMATIC*>( aContainer ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "Tried to create a symbol in {}, which is not a schematic",
aContainer->GetFriendlyName().ToStdString() ) );
return tl::unexpected( e );
}
std::unique_ptr<EDA_ITEM> created = CreateItemForType( aType, aContainer );
if( !created )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
magic_enum::enum_name( aType ) ) );
return tl::unexpected( e );
}
return created;
}
HANDLER_RESULT<ItemRequestStatus> API_HANDLER_SCH::handleCreateUpdateItemsInternal( bool aCreate,
const HANDLER_CONTEXT& aCtx,
const types::ItemHeader &aHeader,
const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
{
ApiResponseStatus e;
auto containerResult = validateItemHeaderDocument( aHeader );
if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
{
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
else if( !containerResult )
{
e.CopyFrom( containerResult.error() );
return tl::unexpected( e );
}
SCH_SCREEN* screen = m_frame->GetScreen();
EE_RTREE& screenItems = screen->Items();
std::map<KIID, EDA_ITEM*> itemUuidMap;
std::for_each( screenItems.begin(), screenItems.end(),
[&]( EDA_ITEM* aItem )
{
itemUuidMap[aItem->m_Uuid] = aItem;
} );
EDA_ITEM* container = nullptr;
if( containerResult->has_value() )
{
const KIID& containerId = **containerResult;
if( itemUuidMap.count( containerId ) )
{
container = itemUuidMap.at( containerId );
if( !container )
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format(
"The requested container {} is not a valid schematic item container",
containerId.AsStdString() ) );
return tl::unexpected( e );
}
}
else
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format(
"The requested container {} does not exist in this document",
containerId.AsStdString() ) );
return tl::unexpected( e );
}
}
COMMIT* commit = getCurrentCommit( aCtx );
for( const google::protobuf::Any& anyItem : aItems )
{
ItemStatus status;
std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
if( !type )
{
status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
status.set_error_message( fmt::format( "Could not decode a valid type from {}",
anyItem.type_url() ) );
aItemHandler( status, anyItem );
continue;
}
HANDLER_RESULT<std::unique_ptr<EDA_ITEM>> creationResult =
createItemForType( *type, container );
if( !creationResult )
{
status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
status.set_error_message( creationResult.error().error_message() );
aItemHandler( status, anyItem );
continue;
}
std::unique_ptr<EDA_ITEM> item( std::move( *creationResult ) );
if( !item->Deserialize( anyItem ) )
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "could not unpack {} from request",
item->GetClass().ToStdString() ) );
return tl::unexpected( e );
}
if( aCreate && itemUuidMap.count( item->m_Uuid ) )
{
status.set_code( ItemStatusCode::ISC_EXISTING );
status.set_error_message( fmt::format( "an item with UUID {} already exists",
item->m_Uuid.AsStdString() ) );
aItemHandler( status, anyItem );
continue;
}
else if( !aCreate && !itemUuidMap.count( item->m_Uuid ) )
{
status.set_code( ItemStatusCode::ISC_NONEXISTENT );
status.set_error_message( fmt::format( "an item with UUID {} does not exist",
item->m_Uuid.AsStdString() ) );
aItemHandler( status, anyItem );
continue;
}
status.set_code( ItemStatusCode::ISC_OK );
google::protobuf::Any newItem;
if( aCreate )
{
item->Serialize( newItem );
commit->Add( item.release() );
if( !m_activeClients.count( aCtx.ClientName ) )
pushCurrentCommit( aCtx, _( "Added items via API" ) );
}
else
{
EDA_ITEM* edaItem = itemUuidMap[item->m_Uuid];
if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( edaItem ) )
{
schItem->SwapData( static_cast<SCH_ITEM*>( item.get() ) );
schItem->Serialize( newItem );
commit->Modify( schItem );
}
else if( LIB_ITEM* libItem = dynamic_cast<LIB_ITEM*>( edaItem ) )
{
// TODO: there is not currently a way to do this, haha
}
else
{
wxASSERT( false );
}
if( !m_activeClients.count( aCtx.ClientName ) )
pushCurrentCommit( aCtx, _( "Created items via API" ) );
}
aItemHandler( status, newItem );
}
return ItemRequestStatus::IRS_OK;
}
void API_HANDLER_SCH::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
const HANDLER_CONTEXT& aCtx )
{
// TODO
}
std::optional<EDA_ITEM*> API_HANDLER_SCH::getItemFromDocument( const DocumentSpecifier& aDocument,
const KIID& aId )
{
if( !validateDocument( aDocument ) )
return std::nullopt;
// TODO
return std::nullopt;
}

View File

@ -0,0 +1,74 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_HANDLER_SCH_H
#define KICAD_API_HANDLER_SCH_H
#include <api/api_handler_editor.h>
#include <api/common/commands/editor_commands.pb.h>
#include <kiid.h>
using namespace kiapi;
using namespace kiapi::common;
class SCH_EDIT_FRAME;
class SCH_ITEM;
class API_HANDLER_SCH : public API_HANDLER_EDITOR
{
public:
API_HANDLER_SCH( SCH_EDIT_FRAME* aFrame );
protected:
std::unique_ptr<COMMIT> createCommit() override;
kiapi::common::types::DocumentType thisDocumentType() const override
{
return kiapi::common::types::DOCTYPE_SCHEMATIC;
}
bool validateDocumentInternal( const DocumentSpecifier& aDocument ) const override;
HANDLER_RESULT<std::unique_ptr<EDA_ITEM>> createItemForType( KICAD_T aType,
EDA_ITEM* aContainer );
HANDLER_RESULT<types::ItemRequestStatus> handleCreateUpdateItemsInternal( bool aCreate,
const HANDLER_CONTEXT& aCtx,
const types::ItemHeader &aHeader,
const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
std::function<void(commands::ItemStatus, google::protobuf::Any)> aItemHandler )
override;
void deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
const HANDLER_CONTEXT& aCtx ) override;
std::optional<EDA_ITEM*> getItemFromDocument( const DocumentSpecifier& aDocument,
const KIID& aId ) override;
private:
HANDLER_RESULT<commands::GetOpenDocumentsResponse> handleGetOpenDocuments(
commands::GetOpenDocuments& aMsg, const HANDLER_CONTEXT& aCtx );
SCH_EDIT_FRAME* m_frame;
};
#endif //KICAD_API_HANDLER_SCH_H

View File

@ -0,0 +1,107 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <lib_field.h>
#include <lib_pin.h>
#include <lib_shape.h>
#include <lib_symbol.h>
#include <lib_text.h>
#include <lib_textbox.h>
#include <sch_bitmap.h>
#include <sch_bus_entry.h>
#include <sch_field.h>
#include <sch_junction.h>
#include <sch_label.h>
#include <sch_line.h>
#include <sch_no_connect.h>
#include <sch_pin.h>
#include <sch_shape.h>
#include <sch_sheet.h>
#include <sch_sheet_pin.h>
#include <sch_table.h>
#include <sch_tablecell.h>
#include <sch_text.h>
#include <sch_textbox.h>
#include "api_sch_utils.h"
std::unique_ptr<EDA_ITEM> CreateItemForType( KICAD_T aType, EDA_ITEM* aContainer )
{
SCH_ITEM* parentSchItem = dynamic_cast<SCH_ITEM*>( aContainer );
LIB_SYMBOL* parentLibSymbol = nullptr;
if( aContainer && aContainer->Type() == LIB_SYMBOL_T )
parentLibSymbol = static_cast<LIB_SYMBOL*>( aContainer );
switch( aType )
{
case SCH_JUNCTION_T: return std::make_unique<SCH_JUNCTION>();
case SCH_NO_CONNECT_T: return std::make_unique<SCH_NO_CONNECT>();
case SCH_BUS_WIRE_ENTRY_T: return std::make_unique<SCH_BUS_WIRE_ENTRY>();
case SCH_BUS_BUS_ENTRY_T: return std::make_unique<SCH_BUS_BUS_ENTRY>();
case SCH_LINE_T: return std::make_unique<SCH_LINE>();
case SCH_SHAPE_T: return std::make_unique<SCH_SHAPE>();
case SCH_BITMAP_T: return std::make_unique<SCH_BITMAP>();
case SCH_TEXTBOX_T: return std::make_unique<SCH_TEXTBOX>();
case SCH_TEXT_T: return std::make_unique<SCH_TEXT>();
case SCH_TABLE_T: return std::make_unique<SCH_TABLE>();
case SCH_TABLECELL_T: return std::make_unique<SCH_TABLECELL>();
case SCH_LABEL_T: return std::make_unique<SCH_LABEL>();
case SCH_GLOBAL_LABEL_T: return std::make_unique<SCH_GLOBALLABEL>();
case SCH_HIER_LABEL_T: return std::make_unique<SCH_HIERLABEL>();
case SCH_DIRECTIVE_LABEL_T: return std::make_unique<SCH_DIRECTIVE_LABEL>();
case SCH_FIELD_T: return std::make_unique<SCH_FIELD>( parentSchItem );
case SCH_SYMBOL_T:
{
// TODO: constructing currently requires more than just a "container" LIB_SYMBOL
return nullptr;
}
case SCH_SHEET_PIN_T:
{
if( aContainer && aContainer->Type() == SCH_SHEET_T )
return std::make_unique<SCH_SHEET_PIN>( static_cast<SCH_SHEET*>( aContainer ) );
return nullptr;
}
case SCH_SHEET_T: return std::make_unique<SCH_SHEET>();
case SCH_PIN_T:
{
// TODO: constructing currently requires LIB_PIN and SCH_SYMBOL ptr,
// or SCH_SYMBOL and number+alt. Need to determine ideal default ctor.
return nullptr;
}
case LIB_SYMBOL_T: return nullptr; // TODO: ctor currently requires non-null name
case LIB_SHAPE_T: return std::make_unique<LIB_SHAPE>( parentLibSymbol );
case LIB_TEXT_T: return std::make_unique<LIB_TEXT>( parentLibSymbol );
case LIB_TEXTBOX_T: return std::make_unique<LIB_TEXTBOX>( parentLibSymbol );
case LIB_PIN_T: return std::make_unique<LIB_PIN>( parentLibSymbol );
case LIB_FIELD_T: return std::make_unique<LIB_FIELD>( parentLibSymbol );
default:
return nullptr;
}
}

View File

@ -0,0 +1,31 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_SCH_UTILS_H
#define KICAD_API_SCH_UTILS_H
#include <memory>
#include <core/typeinfo.h>
class EDA_ITEM;
std::unique_ptr<EDA_ITEM> CreateItemForType( KICAD_T aType, EDA_ITEM* aContainer );
#endif //KICAD_API_SCH_UTILS_H

View File

@ -32,7 +32,7 @@
class LIB_SHAPE : public LIB_ITEM, public EDA_SHAPE
{
public:
LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape, int aLineWidth = 0,
LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape = SHAPE_T::UNDEFINED, int aLineWidth = 0,
FILL_T aFillType = FILL_T::NO_FILL, KICAD_T aType = LIB_SHAPE_T );
// Do not create a copy constructor. The one generated by the compiler is adequate.

View File

@ -326,6 +326,11 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
update = toolsMenu->Add( ACTIONS::updateSchematicFromPcb );
update->Enable( !Kiface().IsSingle() );
#ifdef KICAD_IPC_API
toolsMenu->AppendSeparator();
toolsMenu->Add( ACTIONS::pluginsReload );
#endif
//-- Preferences menu -----------------------------------------------
//
ACTION_MENU* prefsMenu = new ACTION_MENU( false, selTool );

View File

@ -31,6 +31,7 @@
class PICKED_ITEMS_LIST;
class TOOL_MANAGER;
class SCH_EDIT_FRAME;
class SCH_BASE_FRAME;
class EDA_DRAW_FRAME;
class TOOL_BASE;

View File

@ -22,6 +22,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <api/api_handler_sch.h>
#include <api/api_server.h>
#include <base_units.h>
#include <bitmaps.h>
#include <symbol_library.h>
@ -97,6 +99,10 @@
#include <widgets/wx_aui_utils.h>
#include <drawing_sheet/ds_proxy_view_item.h>
#ifdef KICAD_IPC_API
#include <api/api_plugin_manager.h>
#endif
#define DIFF_SYMBOLS_DIALOG_NAME wxT( "DiffSymbolsDialog" )
@ -177,6 +183,16 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
ReCreateVToolbar();
ReCreateOptToolbar();
#ifdef KICAD_IPC_API
wxTheApp->Bind( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED,
[&]( wxCommandEvent& aEvt )
{
wxLogTrace( traceApi, "SCH frame: EDA_EVT_PLUGIN_AVAILABILITY_CHANGED" );
ReCreateHToolbar();
aEvt.Skip();
} );
#endif
m_hierarchy = new HIERARCHY_PANE( this );
// Initialize common print setup dialog settings.
@ -380,6 +396,11 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
updateTitle();
m_toolManager->GetTool<SCH_NAVIGATE_TOOL>()->ResetHistory();
#ifdef KICAD_IPC_API
m_apiHandler = std::make_unique<API_HANDLER_SCH>( this );
Pgm().GetApiServer().RegisterHandler( m_apiHandler.get() );
#endif
// Default shutdown reason until a file is loaded
KIPLATFORM::APP::SetShutdownBlockReason( this, _( "New schematic file is unsaved" ) );

View File

@ -62,6 +62,7 @@ class DIALOG_SYMBOL_FIELDS_TABLE;
class DIALOG_SCH_FIND;
class RESCUER;
class HIERARCHY_PANE;
class API_HANDLER_SCH;
/// Schematic search type used by the socket link with Pcbnew
@ -872,6 +873,11 @@ public:
void ToggleNetNavigator();
PLUGIN_ACTION_SCOPE PluginActionScope() const override
{
return PLUGIN_ACTION_SCOPE::SCHEMATIC;
}
DECLARE_EVENT_TABLE()
protected:
@ -1016,6 +1022,10 @@ private:
bool m_highlightedConnChanged;
std::vector<wxEvtHandler*> m_schematicChangeListeners;
#ifdef KICAD_IPC_API
std::unique_ptr<API_HANDLER_SCH> m_apiHandler;
#endif
};

View File

@ -54,7 +54,8 @@ public:
SCH_FIELD( const VECTOR2I& aPos, int aFieldId, SCH_ITEM* aParent,
const wxString& aName = wxEmptyString );
SCH_FIELD( SCH_ITEM* aParent, int aFieldId, const wxString& aName = wxEmptyString );
SCH_FIELD( SCH_ITEM* aParent, int aFieldId = INVALID_FIELD,
const wxString& aName = wxEmptyString );
SCH_FIELD( const SCH_FIELD& aText );

View File

@ -43,6 +43,9 @@
#include <core/mirror.h>
#include <trigo.h>
#include <sch_label.h>
#include <magic_enum.hpp>
#include <api/api_utils.h>
#include <api/schematic/schematic_types.pb.h>
using KIGFX::SCH_RENDER_SETTINGS;
@ -1470,6 +1473,31 @@ SCH_LABEL::SCH_LABEL( const VECTOR2I& pos, const wxString& text ) :
}
void SCH_LABEL::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::schematic::types::LocalLabel label;
label.mutable_id()->set_value( m_Uuid.AsStdString() );
kiapi::common::PackVector2( *label.mutable_position(), GetPosition() );
aContainer.PackFrom( label );
}
bool SCH_LABEL::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::schematic::types::LocalLabel label;
if( !aContainer.UnpackTo( &label ) )
return false;
const_cast<KIID&>( m_Uuid ) = KIID( label.id().value() );
SetPosition( kiapi::common::UnpackVector2( label.position() ) );
return true;
}
const BOX2I SCH_LABEL::GetBodyBoundingBox() const
{
BOX2I rect = GetTextBox();
@ -1542,6 +1570,19 @@ SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const SCH_DIRECTIVE_LABEL& aClassLabel
}
void SCH_DIRECTIVE_LABEL::Serialize( google::protobuf::Any &aContainer ) const
{
// TODO
}
bool SCH_DIRECTIVE_LABEL::Deserialize( const google::protobuf::Any &aContainer )
{
// TODO
return false;
}
int SCH_DIRECTIVE_LABEL::GetPenWidth() const
{
int pen = 0;
@ -1791,6 +1832,19 @@ SCH_GLOBALLABEL::SCH_GLOBALLABEL( const SCH_GLOBALLABEL& aGlobalLabel ) :
}
void SCH_GLOBALLABEL::Serialize( google::protobuf::Any &aContainer ) const
{
// TODO
}
bool SCH_GLOBALLABEL::Deserialize( const google::protobuf::Any &aContainer )
{
// TODO
return false;
}
VECTOR2I SCH_GLOBALLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
{
int horiz = GetLabelBoxExpansion( aSettings );
@ -1991,6 +2045,19 @@ SCH_HIERLABEL::SCH_HIERLABEL( const VECTOR2I& pos, const wxString& text, KICAD_T
}
void SCH_HIERLABEL::Serialize( google::protobuf::Any &aContainer ) const
{
// TODO
}
bool SCH_HIERLABEL::Deserialize( const google::protobuf::Any &aContainer )
{
// TODO
return false;
}
void SCH_HIERLABEL::SetSpinStyle( SPIN_STYLE aSpinStyle )
{
SCH_LABEL_BASE::SetSpinStyle( aSpinStyle );

View File

@ -377,6 +377,9 @@ public:
~SCH_LABEL() { }
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && SCH_LABEL_T == aItem->Type();
@ -426,6 +429,9 @@ public:
~SCH_DIRECTIVE_LABEL() { }
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && SCH_DIRECTIVE_LABEL_T == aItem->Type();
@ -483,6 +489,9 @@ public:
~SCH_GLOBALLABEL() { }
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && SCH_GLOBAL_LABEL_T == aItem->Type();
@ -542,6 +551,9 @@ public:
~SCH_HIERLABEL() { }
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && SCH_HIER_LABEL_T == aItem->Type();

View File

@ -38,6 +38,9 @@
#include <project/net_settings.h>
#include <trigo.h>
#include <board_item.h>
#include <api/api_enums.h>
#include <api/api_utils.h>
#include <api/schematic/schematic_types.pb.h>
SCH_LINE::SCH_LINE( const VECTOR2I& pos, int layer ) :
@ -90,6 +93,49 @@ SCH_LINE::SCH_LINE( const SCH_LINE& aLine ) :
}
void SCH_LINE::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::schematic::types::Line line;
line.mutable_id()->set_value( m_Uuid.AsStdString() );
kiapi::common::PackVector2( *line.mutable_start(), GetStartPoint() );
kiapi::common::PackVector2( *line.mutable_end(), GetEndPoint() );
line.set_layer(
ToProtoEnum<SCH_LAYER_ID, kiapi::schematic::types::SchematicLayer>( GetLayer() ) );
aContainer.PackFrom( line );
}
bool SCH_LINE::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::schematic::types::Line line;
if( !aContainer.UnpackTo( &line ) )
return false;
const_cast<KIID&>( m_Uuid ) = KIID( line.id().value() );
SetStartPoint( kiapi::common::UnpackVector2( line.start() ) );
SetEndPoint( kiapi::common::UnpackVector2( line.end() ) );
SCH_LAYER_ID layer =
FromProtoEnum<SCH_LAYER_ID, kiapi::schematic::types::SchematicLayer>( line.layer() );
switch( layer )
{
case LAYER_WIRE:
case LAYER_BUS:
case LAYER_NOTES:
SetLayer( layer );
break;
default:
break;
}
return true;
}
wxString SCH_LINE::GetFriendlyName() const
{
switch( GetLayer() )

View File

@ -51,6 +51,9 @@ public:
~SCH_LINE() { }
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && SCH_LINE_T == aItem->Type();

View File

@ -32,7 +32,8 @@
class SCH_SHAPE : public SCH_ITEM, public EDA_SHAPE
{
public:
SCH_SHAPE( SHAPE_T aShape, int aLineWidth = 0, FILL_T aFillType = FILL_T::NO_FILL,
SCH_SHAPE( SHAPE_T aShape = SHAPE_T::UNDEFINED, int aLineWidth = 0,
FILL_T aFillType = FILL_T::NO_FILL,
KICAD_T aType = SCH_SHAPE_T );
// Do not create a copy constructor. The one generated by the compiler is adequate.

View File

@ -32,7 +32,7 @@
class SCH_TABLE : public SCH_ITEM
{
public:
SCH_TABLE( int aLineWidth );
SCH_TABLE( int aLineWidth = 0 );
SCH_TABLE( const SCH_TABLE& aTable );

View File

@ -25,11 +25,13 @@
*/
#include <advanced_config.h>
#include <api/api_plugin_manager.h>
#include <sch_draw_panel.h>
#include <sch_edit_frame.h>
#include <kiface_base.h>
#include <bitmaps.h>
#include <eeschema_id.h>
#include <pgm_base.h>
#include <python_scripting.h>
#include <tool/tool_manager.h>
#include <tool/action_toolbar.h>
@ -119,11 +121,24 @@ void SCH_EDIT_FRAME::ReCreateHToolbar()
m_mainToolBar->AddScaledSeparator( this );
m_mainToolBar->Add( EE_ACTIONS::showPcbNew );
// Access to the scripting console
if( SCRIPTING::IsWxAvailable() )
// Add scripting console and API plugins
bool scriptingAvailable = SCRIPTING::IsWxAvailable();
#ifdef KICAD_IPC_API
bool haveApiPlugins = Pgm().GetCommonSettings()->m_Api.enable_server &&
!Pgm().GetPluginManager().GetActionsForScope( PLUGIN_ACTION_SCOPE::SCHEMATIC ).empty();
#else
bool haveApiPlugins = false;
#endif
if( scriptingAvailable || haveApiPlugins )
{
m_mainToolBar->AddScaledSeparator( this );
m_mainToolBar->Add( EE_ACTIONS::showPythonConsole, ACTION_TOOLBAR::TOGGLE );
if( scriptingAvailable )
m_mainToolBar->Add( EE_ACTIONS::showPythonConsole, ACTION_TOOLBAR::TOGGLE );
if( haveApiPlugins )
addApiPluginTools();
}
// after adding the tools to the toolbar, must call Realize() to reflect the changes

View File

@ -80,6 +80,10 @@
#include <wx/treectrl.h>
#include "sch_edit_table_tool.h"
#ifdef KICAD_IPC_API
#include <api/api_plugin_manager.h>
#endif
/**
* Flag to enable schematic paste debugging output.
@ -2537,6 +2541,15 @@ int SCH_EDITOR_CONTROL::TogglePythonConsole( const TOOL_EVENT& aEvent )
}
int SCH_EDITOR_CONTROL::ReloadPlugins( const TOOL_EVENT& aEvent )
{
#ifdef KICAD_IPC_API
Pgm().GetPluginManager().ReloadPlugins();
#endif
return 0;
}
int SCH_EDITOR_CONTROL::RepairSchematic( const TOOL_EVENT& aEvent )
{
int errors = 0;
@ -2731,6 +2744,7 @@ void SCH_EDITOR_CONTROL::setTransitions()
Go( &SCH_EDITOR_CONTROL::ToggleAnnotateAuto, EE_ACTIONS::toggleAnnotateAuto.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::TogglePythonConsole, EE_ACTIONS::showPythonConsole.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ReloadPlugins, ACTIONS::pluginsReload.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::RepairSchematic, EE_ACTIONS::repairSchematic.MakeEvent() );

View File

@ -143,6 +143,7 @@ public:
int ToggleAnnotateAuto( const TOOL_EVENT& aEvent );
int ToggleAnnotateRecursive( const TOOL_EVENT& aEvent );
int TogglePythonConsole( const TOOL_EVENT& aEvent );
int ReloadPlugins( const TOOL_EVENT& aEvent );
int GridFeedback( const TOOL_EVENT& aEvent );

View File

@ -467,6 +467,10 @@ int SYMBOL_EDITOR_MOVE_TOOL::AlignElements( const TOOL_EVENT& aEvent )
doMoveItem( shape, newStart - shape->GetStart() );
break;
case SHAPE_T::UNDEFINED:
wxASSERT_MSG( false, wxT( "Undefined shape in AlignElements" ) );
break;
}
}
else

29
include/api/api_enums.h Normal file
View File

@ -0,0 +1,29 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_ENUMS_H
#define KICAD_API_ENUMS_H
template<typename KiCadEnum, typename ProtoEnum>
KiCadEnum FromProtoEnum( ProtoEnum aEnumValue );
template<typename KiCadEnum, typename ProtoEnum>
ProtoEnum ToProtoEnum( KiCadEnum aEnumValue );
#endif //KICAD_API_ENUMS_H

View File

@ -32,7 +32,7 @@
#include <google/protobuf/message.h>
#include <import_export.h>
#include <kicommon.h>
#include <api/common/envelope.pb.h>
#include <core/typeinfo.h>
@ -44,11 +44,20 @@ typedef tl::expected<ApiResponse, ApiResponseStatus> API_RESULT;
template <typename T>
using HANDLER_RESULT = tl::expected<T, ApiResponseStatus>;
class API_HANDLER
struct HANDLER_CONTEXT
{
std::string ClientName;
};
class KICOMMON_API API_HANDLER
{
public:
API_HANDLER() {}
virtual ~API_HANDLER() {}
/**
* Attempt to handle the given API request, if a handler exists in this class for the message.
* @param aMsg is a request to attempt to handle
@ -56,8 +65,6 @@ public:
*/
API_RESULT Handle( ApiRequest& aMsg );
static std::optional<KICAD_T> TypeNameFromAny( const google::protobuf::Any& aMessage );
protected:
/**
@ -81,7 +88,9 @@ protected:
* @param aHandler is the handler function for the given request and response types
*/
template <class RequestType, class ResponseType, class HandlerType>
void registerHandler( HANDLER_RESULT<ResponseType>( HandlerType::* aHandler )( RequestType& ) )
void registerHandler(
HANDLER_RESULT<ResponseType>( HandlerType::* aHandler )( RequestType&,
const HANDLER_CONTEXT& ) )
{
std::string typeName = RequestType().GetTypeName();
@ -91,14 +100,17 @@ protected:
m_handlers[typeName] =
[=]( ApiRequest& aRequest ) -> API_RESULT
{
RequestType command;
RequestType cmd;
ApiResponse envelope;
if( !tryUnpack( aRequest, envelope, command ) )
if( !tryUnpack( aRequest, envelope, cmd ) )
return envelope;
HANDLER_CONTEXT ctx;
ctx.ClientName = aRequest.header().client_name();
HANDLER_RESULT<ResponseType> response =
std::invoke( aHandler, static_cast<HandlerType*>( this ), command );
std::invoke( aHandler, static_cast<HandlerType*>( this ), cmd, ctx );
if( response.has_value() )
{
@ -116,6 +128,8 @@ protected:
/// Maps type name (without the URL prefix) to a handler method
std::map<std::string, REQUEST_HANDLER> m_handlers;
static const wxString m_defaultCommitMessage;
private:
template<typename MessageType>

View File

@ -21,22 +21,29 @@
#ifndef KICAD_API_HANDLER_COMMON_H
#define KICAD_API_HANDLER_COMMON_H
#include <google/protobuf/empty.pb.h>
#include <api/api_handler.h>
#include <api/common/commands/base_commands.pb.h>
#include <api/common/commands/editor_commands.pb.h>
#include <google/protobuf/empty.pb.h>
#include <api/common/commands/project_commands.pb.h>
using namespace kiapi;
using namespace kiapi::common;
using google::protobuf::Empty;
class API_HANDLER_COMMON : public API_HANDLER
class KICOMMON_API API_HANDLER_COMMON : public API_HANDLER
{
public:
API_HANDLER_COMMON();
private:
HANDLER_RESULT<commands::GetVersionResponse> handleGetVersion( commands::GetVersion& aMsg );
HANDLER_RESULT<commands::GetVersionResponse> handleGetVersion( commands::GetVersion& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::NetClassesResponse> handleGetNetClasses( commands::GetNetClasses& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<Empty> handlePing( commands::Ping& aMsg, const HANDLER_CONTEXT& aCtx );
};
#endif //KICAD_API_HANDLER_COMMON_H

View File

@ -0,0 +1,115 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_HANDLER_EDITOR_H
#define KICAD_API_HANDLER_EDITOR_H
#include <api/api_handler.h>
#include <api/common/commands/editor_commands.pb.h>
#include <commit.h>
#include <kiid.h>
using namespace kiapi::common;
using kiapi::common::types::DocumentSpecifier;
using kiapi::common::types::ItemRequestStatus;
using kiapi::common::commands::ItemDeletionStatus;
class EDA_BASE_FRAME;
/**
* Base class for API handlers related to editor frames
*/
class API_HANDLER_EDITOR : public API_HANDLER
{
public:
API_HANDLER_EDITOR( EDA_BASE_FRAME* aFrame = nullptr );
protected:
/// If the header is valid, returns the item container
HANDLER_RESULT<std::optional<KIID>> validateItemHeaderDocument(
const kiapi::common::types::ItemHeader& aHeader );
HANDLER_RESULT<bool> validateDocument( const DocumentSpecifier& aDocument );
/**
* Checks if the editor can accept commands
* @return an error status if busy, std::nullopt if not busy
*/
virtual std::optional<ApiResponseStatus> checkForBusy();
HANDLER_RESULT<commands::BeginCommitResponse> handleBeginCommit( commands::BeginCommit& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::EndCommitResponse> handleEndCommit( commands::EndCommit& aMsg,
const HANDLER_CONTEXT& aCtx );
COMMIT* getCurrentCommit( const HANDLER_CONTEXT& aCtx );
virtual void pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage );
HANDLER_RESULT<commands::CreateItemsResponse> handleCreateItems( commands::CreateItems& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::UpdateItemsResponse> handleUpdateItems( commands::UpdateItems& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::DeleteItemsResponse> handleDeleteItems( commands::DeleteItems& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::HitTestResponse> handleHitTest( commands::HitTest& aMsg,
const HANDLER_CONTEXT& aCtx );
/**
* Override this to create an appropriate COMMIT subclass for the frame in question
* @return a new COMMIT, bound to the editor frame
*/
virtual std::unique_ptr<COMMIT> createCommit() = 0;
/**
* Override this to specify which document type this editor handles
*/
virtual kiapi::common::types::DocumentType thisDocumentType() const = 0;
/**
* @return true if the given document is valid for this editor and is currently open
*/
virtual bool validateDocumentInternal( const DocumentSpecifier& aDocument ) const = 0;
virtual HANDLER_RESULT<ItemRequestStatus> handleCreateUpdateItemsInternal( bool aCreate,
const HANDLER_CONTEXT& aCtx,
const types::ItemHeader &aHeader,
const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
std::function<void( commands::ItemStatus, google::protobuf::Any )> aItemHandler ) = 0;
virtual void deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
const HANDLER_CONTEXT& aCtx ) = 0;
virtual std::optional<EDA_ITEM*> getItemFromDocument( const DocumentSpecifier& aDocument,
const KIID& aId ) = 0;
protected:
std::map<std::string, std::pair<KIID, std::unique_ptr<COMMIT>>> m_commits;
std::set<std::string> m_activeClients;
EDA_BASE_FRAME* m_frame;
};
#endif //KICAD_API_HANDLER_EDITOR_H

View File

@ -58,7 +58,7 @@ enum class PLUGIN_ACTION_SCOPE
SCHEMATIC,
FOOTPRINT,
SYMBOL,
KICAD
PROJECT_MANAGER
};

View File

@ -32,7 +32,7 @@
*
* Use "KICAD_API" to enable.
*
* @ingroup traceApi
* @ingroup trace_env_vars
*/
extern const KICOMMON_API wxChar* const traceApi;
@ -41,6 +41,7 @@ wxDECLARE_EVENT( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxCommandEvent );
/// Notifies other parts of KiCad when plugin availability changes
extern const KICOMMON_API wxEventTypeTag<wxCommandEvent> EDA_EVT_PLUGIN_AVAILABILITY_CHANGED;
/**
* Responsible for loading plugin definitions for API-based plugins (ones that do not run inside
* KiCad itself, but instead are launched as external processes by KiCad)

View File

@ -1,8 +1,8 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -28,6 +28,8 @@
#include <wx/event.h>
#include <wx/filename.h>
#include <kicommon.h>
class API_HANDLER;
class API_HANDLER_COMMON;
class KINNG_REQUEST_SERVER;
@ -37,7 +39,7 @@ class wxEvtHandler;
wxDECLARE_EVENT( API_REQUEST_EVENT, wxCommandEvent );
class KICAD_API_SERVER : public wxEvtHandler
class KICOMMON_API KICAD_API_SERVER : public wxEvtHandler
{
public:
KICAD_API_SERVER();

48
include/api/api_utils.h Normal file
View File

@ -0,0 +1,48 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_UTILS_H
#define KICAD_API_UTILS_H
#include <optional>
#include <core/typeinfo.h>
#include <lib_id.h>
#include <api/common/types/base_types.pb.h>
#include <google/protobuf/any.pb.h>
#include <layer_ids.h>
#include <math/vector2d.h>
namespace kiapi::common
{
std::optional<KICAD_T> TypeNameFromAny( const google::protobuf::Any& aMessage );
LIB_ID LibIdFromProto( const types::LibraryIdentifier& aId );
types::LibraryIdentifier LibIdToProto( const LIB_ID& aId );
void PackVector2( kiapi::common::types::Vector2& aOutput, const VECTOR2I aInput );
VECTOR2I UnpackVector2( const types::Vector2& aInput );
} // namespace kiapi::common
#endif //KICAD_API_UTILS_H

View File

@ -0,0 +1,60 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_SERIALIZABLE_H
#define KICAD_SERIALIZABLE_H
#include <wx/debug.h>
namespace google {
namespace protobuf {
class Any;
}
}
/**
* Interface for objects that can be serialized to Protobuf messages
*/
class SERIALIZABLE
{
public:
/**
* Serializes this object to the given Any message.
* The Any message's concrete type will be specific to the object in question.
* @param aContainer will be filled with a message describing this object
*/
virtual void Serialize( google::protobuf::Any &aContainer ) const
{
wxASSERT_MSG( false, wxS( "Serialize called on an object that doesn't implement it!" ) );
}
/**
* Deserializes the given protobuf message into this object.
* @param aContainer is an Any which should have a concrete type matching this object
* @return true if unpacking and deserialization succeeded
*/
virtual bool Deserialize( const google::protobuf::Any &aContainer )
{
wxASSERT_MSG( false, wxS( "Deserialize called on an object that doesn't implement it!" ) );
return false;
}
};
#endif //KICAD_SERIALIZABLE_H

View File

@ -600,6 +600,13 @@ public:
*/
virtual void HandleSystemColorChange();
/**
* Checks if this frame is ready to accept API commands.
* A frame might not accept commands if a long-running process is underway, a dialog is open,
* the user is interacting with a tool, etc.
*/
virtual bool CanAcceptApiCommands() { return IsEnabled(); }
protected:
///< Default style flags used for wxAUI toolbars.
static constexpr int KICAD_AUI_TB_STYLE = wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_PLAIN_BACKGROUND;

View File

@ -26,6 +26,7 @@
#ifndef DRAW_FRAME_H_
#define DRAW_FRAME_H_
#include <api/api_plugin.h>
#include <eda_base_frame.h>
#include <kiway_player.h>
#include <gal/gal_display_options.h>
@ -490,6 +491,13 @@ public:
*/
bool SaveCanvasImageToFile( const wxString& aFileName, BITMAP_TYPE aBitmapType );
/**
* Handler for activating an API plugin (via toolbar or menu)
*/
virtual void OnApiPluginInvoke( wxCommandEvent& aEvent );
virtual PLUGIN_ACTION_SCOPE PluginActionScope() const { return PLUGIN_ACTION_SCOPE::INVALID; }
DECLARE_EVENT_TABLE()
@ -525,6 +533,11 @@ protected:
virtual void handleActivateEvent( wxActivateEvent& aEvent );
void onActivate( wxActivateEvent& aEvent );
/**
* Append actions from API plugins to the main toolbar
*/
virtual void addApiPluginTools();
wxSocketServer* m_socketServer;

View File

@ -29,6 +29,7 @@
#include <deque>
#include <api/serializable.h>
#include <core/typeinfo.h>
#include <eda_item_flags.h>
#include <eda_search_data.h>
@ -83,7 +84,7 @@ typedef const INSPECTOR_FUNC& INSPECTOR;
/**
* A base class for most all the KiCad significant classes used in schematics and boards.
*/
class EDA_ITEM : public KIGFX::VIEW_ITEM
class EDA_ITEM : public KIGFX::VIEW_ITEM, public SERIALIZABLE
{
public:
virtual ~EDA_ITEM() { };
@ -438,10 +439,6 @@ public:
virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
virtual void Serialize( google::protobuf::Any &aContainer ) const {}
virtual bool Deserialize( const google::protobuf::Any &aContainer ) { return false; }
#if defined(DEBUG)
/**

View File

@ -40,6 +40,7 @@ using KIGFX::COLOR4D;
enum class SHAPE_T : int
{
UNDEFINED = -1,
SEGMENT = 0,
RECTANGLE, /// use RECTANGLE instead of RECT to avoid collision in a Windows header
ARC,

View File

@ -38,6 +38,7 @@ class FONT;
// NB: values -1,0,1 are used in computations, do not change them
//
// This is API surface mapped to common.types.HorizontalAlignment
enum GR_TEXT_H_ALIGN_T
{
GR_TEXT_H_ALIGN_LEFT = -1,
@ -46,6 +47,7 @@ enum GR_TEXT_H_ALIGN_T
GR_TEXT_H_ALIGN_INDETERMINATE
};
// This is API surface mapped to common.types.VertialAlignment
enum GR_TEXT_V_ALIGN_T
{
GR_TEXT_V_ALIGN_TOP = -1,

View File

@ -40,6 +40,7 @@ class TEMPLATE_FIELDNAMES_LEXER;
* an unlimited number of user defined fields, only some of which have indices defined here.
*/
enum MANDATORY_FIELD_T {
INVALID_FIELD = -1, ///< The field ID hasn't been set yet; field is invalid
REFERENCE_FIELD = 0, ///< Field Reference of part, i.e. "IC21"
VALUE_FIELD, ///< Field Value of part, i.e. "3.3K"
FOOTPRINT_FIELD, ///< Field Name Module PCB, i.e. "16DIP300"

View File

@ -220,6 +220,9 @@ public:
static TOOL_ACTION getInvolved;
static TOOL_ACTION reportBug;
// API
static TOOL_ACTION pluginsReload;
///< Cursor control event types
enum CURSOR_EVENT_TYPE
{

View File

@ -270,6 +270,14 @@ public:
doRunAction( aAction, false, a, nullptr );
}
bool PostAction( const TOOL_ACTION& aAction, COMMIT* aCommit )
{
// Default initialize the parameter argument to an empty std::any
std::any a;
return doRunAction( aAction, false, a, aCommit );
}
/**
* Send a cancel event to the tool currently at the top of the tool stack.
*/

View File

@ -21,10 +21,26 @@
#include <magic_enum.hpp>
#include <api/api_handler_pcb.h>
#include <api/api_pcb_utils.h>
#include <api/api_enums.h>
#include <api/api_utils.h>
#include <board_commit.h>
#include <board_design_settings.h>
#include <footprint.h>
#include <netinfo.h>
#include <pad.h>
#include <pcb_edit_frame.h>
#include <pcb_group.h>
#include <pcb_reference_image.h>
#include <pcb_shape.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_track.h>
#include <project.h>
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_selection_tool.h>
#include <zone.h>
#include <api/common/types/base_types.pb.h>
@ -33,34 +49,44 @@ using kiapi::common::types::CommandStatus;
using kiapi::common::types::DocumentType;
using kiapi::common::types::ItemRequestStatus;
static const wxString s_defaultCommitMessage = wxS( "Modification from API" );
API_HANDLER_PCB::API_HANDLER_PCB( PCB_EDIT_FRAME* aFrame ) :
API_HANDLER(),
m_frame( aFrame ),
m_transactionInProgress( false )
API_HANDLER_EDITOR( aFrame )
{
registerHandler<RunAction, RunActionResponse>( &API_HANDLER_PCB::handleRunAction );
registerHandler<GetOpenDocuments, GetOpenDocumentsResponse>(
&API_HANDLER_PCB::handleGetOpenDocuments );
registerHandler<BeginCommit, BeginCommitResponse>( &API_HANDLER_PCB::handleBeginCommit );
registerHandler<EndCommit, EndCommitResponse>( &API_HANDLER_PCB::handleEndCommit );
registerHandler<CreateItems, CreateItemsResponse>( &API_HANDLER_PCB::handleCreateItems );
registerHandler<GetItems, GetItemsResponse>( &API_HANDLER_PCB::handleGetItems );
registerHandler<UpdateItems, UpdateItemsResponse>( &API_HANDLER_PCB::handleUpdateItems );
registerHandler<DeleteItems, DeleteItemsResponse>( &API_HANDLER_PCB::handleDeleteItems );
registerHandler<GetBoardStackup, BoardStackupResponse>( &API_HANDLER_PCB::handleGetStackup );
registerHandler<GetGraphicsDefaults, GraphicsDefaultsResponse>(
&API_HANDLER_PCB::handleGetGraphicsDefaults );
registerHandler<GetTextExtents, commands::BoundingBoxResponse>(
&API_HANDLER_PCB::handleGetTextExtents );
registerHandler<InteractiveMoveItems, Empty>( &API_HANDLER_PCB::handleInteractiveMoveItems );
registerHandler<GetNets, NetsResponse>( &API_HANDLER_PCB::handleGetNets );
registerHandler<RefillZones, Empty>( &API_HANDLER_PCB::handleRefillZones );
}
HANDLER_RESULT<RunActionResponse> API_HANDLER_PCB::handleRunAction( RunAction& aRequest )
PCB_EDIT_FRAME* API_HANDLER_PCB::frame() const
{
return static_cast<PCB_EDIT_FRAME*>( m_frame );
}
HANDLER_RESULT<RunActionResponse> API_HANDLER_PCB::handleRunAction( RunAction& aRequest,
const HANDLER_CONTEXT& )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
RunActionResponse response;
if( m_frame->GetToolManager()->RunAction( aRequest.action(), true ) )
if( frame()->GetToolManager()->RunAction( aRequest.action(), true ) )
response.set_status( RunActionStatus::RAS_OK );
else
response.set_status( RunActionStatus::RAS_INVALID );
@ -70,7 +96,7 @@ HANDLER_RESULT<RunActionResponse> API_HANDLER_PCB::handleRunAction( RunAction& a
HANDLER_RESULT<GetOpenDocumentsResponse> API_HANDLER_PCB::handleGetOpenDocuments(
GetOpenDocuments& aMsg )
GetOpenDocuments& aMsg, const HANDLER_CONTEXT& )
{
if( aMsg.type() != DocumentType::DOCTYPE_PCB )
{
@ -83,166 +109,178 @@ HANDLER_RESULT<GetOpenDocumentsResponse> API_HANDLER_PCB::handleGetOpenDocuments
GetOpenDocumentsResponse response;
common::types::DocumentSpecifier doc;
wxFileName fn( m_frame->GetCurrentFileName() );
wxFileName fn( frame()->GetCurrentFileName() );
doc.set_type( DocumentType::DOCTYPE_PCB );
doc.set_board_filename( fn.GetFullName() );
doc.mutable_project()->set_name( frame()->Prj().GetProjectName().ToStdString() );
doc.mutable_project()->set_path( frame()->Prj().GetProjectDirectory().ToStdString() );
response.mutable_documents()->Add( std::move( doc ) );
return response;
}
HANDLER_RESULT<BeginCommitResponse> API_HANDLER_PCB::handleBeginCommit( BeginCommit& aMsg )
void API_HANDLER_PCB::pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage )
{
BeginCommitResponse response;
if( m_commit )
{
// TODO: right now there is no way for m_transactionInProgress to be true here, but
// we should still check it as a safety measure and return a specific error
//if( !m_transactionInProgress )
m_commit->Revert();
}
m_commit.reset( new BOARD_COMMIT( m_frame ) );
// TODO: return an opaque ID for this new commit to make this more robust
m_transactionInProgress = true;
return response;
API_HANDLER_EDITOR::pushCurrentCommit( aCtx, aMessage );
frame()->Refresh();
}
HANDLER_RESULT<EndCommitResponse> API_HANDLER_PCB::handleEndCommit( EndCommit& aMsg )
std::unique_ptr<COMMIT> API_HANDLER_PCB::createCommit()
{
EndCommitResponse response;
// TODO: return more specific error if m_transactionInProgress is false
if( !m_transactionInProgress )
{
// Make sure we don't get stuck with a commit we can never push
m_commit.reset();
response.set_result( CommitResult::CR_NO_COMMIT );
return response;
}
if( !m_commit )
{
response.set_result( CommitResult::CR_NO_COMMIT );
return response;
}
pushCurrentCommit( aMsg.message() );
m_transactionInProgress = false;
response.set_result( CommitResult::CR_OK );
return response;
return std::make_unique<BOARD_COMMIT>( frame() );
}
BOARD_COMMIT* API_HANDLER_PCB::getCurrentCommit()
std::optional<BOARD_ITEM*> API_HANDLER_PCB::getItemById( const KIID& aId ) const
{
if( !m_commit )
m_commit.reset( new BOARD_COMMIT( m_frame ) );
BOARD_ITEM* item = frame()->GetBoard()->GetItem( aId );
return m_commit.get();
if( item == DELETED_BOARD_ITEM::GetInstance() )
return std::nullopt;
return item;
}
void API_HANDLER_PCB::pushCurrentCommit( const std::string& aMessage )
bool API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
{
wxCHECK( m_commit, /* void */ );
wxString msg( aMessage.c_str(), wxConvUTF8 );
if( msg.IsEmpty() )
msg = s_defaultCommitMessage;
m_commit->Push( msg );
m_commit.reset();
m_frame->Refresh();
}
bool API_HANDLER_PCB::validateItemHeaderDocument( const common::types::ItemHeader& aHeader )
{
// TODO: this should return a more complex error type.
// We should provide detailed feedback when a header fails validation, and distinguish between
// "skip this handler" and "this is the right handler, but the request is invalid"
if( !aHeader.has_document() || aHeader.document().type() != DocumentType::DOCTYPE_PCB )
if( aDocument.type() != DocumentType::DOCTYPE_PCB )
return false;
wxFileName fn( m_frame->GetCurrentFileName() );
return aHeader.document().board_filename().compare( fn.GetFullName() ) == 0;
wxFileName fn( frame()->GetCurrentFileName() );
return 0 == aDocument.board_filename().compare( fn.GetFullName() );
}
std::unique_ptr<BOARD_ITEM> API_HANDLER_PCB::createItemForType( KICAD_T aType,
BOARD_ITEM_CONTAINER* aContainer )
HANDLER_RESULT<std::unique_ptr<BOARD_ITEM>> API_HANDLER_PCB::createItemForType( KICAD_T aType,
BOARD_ITEM_CONTAINER* aContainer )
{
switch( aType )
if( !aContainer )
{
case PCB_TRACE_T: return std::make_unique<PCB_TRACK>( aContainer );
case PCB_ARC_T: return std::make_unique<PCB_ARC>( aContainer );
case PCB_VIA_T: return std::make_unique<PCB_VIA>( aContainer );
default: return nullptr;
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "Tried to create an item in a null container" );
return tl::unexpected( e );
}
if( aType == PCB_PAD_T && !dynamic_cast<FOOTPRINT*>( aContainer ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "Tried to create a pad in {}, which is not a footprint",
aContainer->GetFriendlyName().ToStdString() ) );
return tl::unexpected( e );
}
else if( aType == PCB_FOOTPRINT_T && !dynamic_cast<BOARD*>( aContainer ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "Tried to create a footprint in {}, which is not a board",
aContainer->GetFriendlyName().ToStdString() ) );
return tl::unexpected( e );
}
std::unique_ptr<BOARD_ITEM> created = CreateItemForType( aType, aContainer );
if( !created )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
magic_enum::enum_name( aType ) ) );
return tl::unexpected( e );
}
return created;
}
HANDLER_RESULT<CreateItemsResponse> API_HANDLER_PCB::handleCreateItems( CreateItems& aMsg )
HANDLER_RESULT<ItemRequestStatus> API_HANDLER_PCB::handleCreateUpdateItemsInternal( bool aCreate,
const HANDLER_CONTEXT& aCtx,
const types::ItemHeader &aHeader,
const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
{
ApiResponseStatus e;
if( !validateItemHeaderDocument( aMsg.header() ) )
auto containerResult = validateItemHeaderDocument( aHeader );
if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
{
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
BOARD* board = m_frame->GetBoard();
BOARD_ITEM_SET boardItems = board->GetItemSet();
std::map<KIID, BOARD_ITEM*> itemUuidMap;
std::for_each( boardItems.begin(), boardItems.end(),
[&]( BOARD_ITEM* aItem )
{
itemUuidMap[aItem->m_Uuid] = aItem;
} );
BOARD_COMMIT* commit = getCurrentCommit();
CreateItemsResponse response;
for( const google::protobuf::Any& anyItem : aMsg.items() )
else if( !containerResult )
{
ItemCreationResult itemResult;
e.CopyFrom( containerResult.error() );
return tl::unexpected( e );
}
BOARD* board = frame()->GetBoard();
BOARD_ITEM_CONTAINER* container = board;
if( containerResult->has_value() )
{
const KIID& containerId = **containerResult;
std::optional<BOARD_ITEM*> optItem = getItemById( containerId );
if( optItem )
{
container = dynamic_cast<BOARD_ITEM_CONTAINER*>( *optItem );
if( !container )
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format(
"The requested container {} is not a valid board item container",
containerId.AsStdString() ) );
return tl::unexpected( e );
}
}
else
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format(
"The requested container {} does not exist in this document",
containerId.AsStdString() ) );
return tl::unexpected( e );
}
}
BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aCtx ) );
for( const google::protobuf::Any& anyItem : aItems )
{
ItemStatus status;
std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
if( !type )
{
itemResult.set_status( ItemCreationStatus::ICS_INVALID_TYPE );
response.mutable_created_items()->Add( std::move( itemResult ) );
status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
status.set_error_message( fmt::format( "Could not decode a valid type from {}",
anyItem.type_url() ) );
aItemHandler( status, anyItem );
continue;
}
std::unique_ptr<BOARD_ITEM> item = createItemForType( *type, board );
HANDLER_RESULT<std::unique_ptr<BOARD_ITEM>> creationResult =
createItemForType( *type, container );
if( !item )
if( !creationResult )
{
itemResult.set_status( ItemCreationStatus::ICS_INVALID_TYPE );
e.set_error_message( fmt::format( "item type {} not supported for board",
magic_enum::enum_name( *type ) ) );
response.mutable_created_items()->Add( std::move( itemResult ) );
status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
status.set_error_message( creationResult.error().error_message() );
aItemHandler( status, anyItem );
continue;
}
std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
if( !item->Deserialize( anyItem ) )
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
@ -251,28 +289,70 @@ HANDLER_RESULT<CreateItemsResponse> API_HANDLER_PCB::handleCreateItems( CreateIt
return tl::unexpected( e );
}
if( itemUuidMap.count( item->m_Uuid ) )
std::optional<BOARD_ITEM*> optItem = getItemById( item->m_Uuid );
if( aCreate && optItem )
{
itemResult.set_status( ItemCreationStatus::ICS_EXISTING );
response.mutable_created_items()->Add( std::move( itemResult ) );
status.set_code( ItemStatusCode::ISC_EXISTING );
status.set_error_message( fmt::format( "an item with UUID {} already exists",
item->m_Uuid.AsStdString() ) );
aItemHandler( status, anyItem );
continue;
}
else if( !aCreate && !optItem )
{
status.set_code( ItemStatusCode::ISC_NONEXISTENT );
status.set_error_message( fmt::format( "an item with UUID {} does not exist",
item->m_Uuid.AsStdString() ) );
aItemHandler( status, anyItem );
continue;
}
itemResult.set_status( ItemCreationStatus::ICS_OK );
item->Serialize( *itemResult.mutable_item() );
commit->Add( item.release() );
if( aCreate && !board->GetEnabledLayers().Contains( item->GetLayer() ) )
{
status.set_code( ItemStatusCode::ISC_INVALID_DATA );
status.set_error_message( fmt::format( "attempted to add item on disabled layer {}",
LayerName( item->GetLayer() ).ToStdString() ) );
aItemHandler( status, anyItem );
continue;
}
response.mutable_created_items()->Add( std::move( itemResult ) );
status.set_code( ItemStatusCode::ISC_OK );
google::protobuf::Any newItem;
if( aCreate )
{
item->Serialize( newItem );
commit->Add( item.release() );
}
else
{
BOARD_ITEM* boardItem = *optItem;
commit->Modify( boardItem );
boardItem->SwapItemData( item.get() );
boardItem->Serialize( newItem );
}
aItemHandler( status, newItem );
}
pushCurrentCommit( "Added items via API" );
response.set_status( ItemRequestStatus::IRS_OK );
return response;
if( !m_activeClients.count( aCtx.ClientName ) )
{
pushCurrentCommit( aCtx, aCreate ? _( "Created items via API" )
: _( "Added items via API" ) );
}
return ItemRequestStatus::IRS_OK;
}
HANDLER_RESULT<GetItemsResponse> API_HANDLER_PCB::handleGetItems( GetItems& aMsg )
HANDLER_RESULT<GetItemsResponse> API_HANDLER_PCB::handleGetItems( GetItems& aMsg,
const HANDLER_CONTEXT& )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
if( !validateItemHeaderDocument( aMsg.header() ) )
{
ApiResponseStatus e;
@ -283,18 +363,17 @@ HANDLER_RESULT<GetItemsResponse> API_HANDLER_PCB::handleGetItems( GetItems& aMsg
GetItemsResponse response;
BOARD* board = m_frame->GetBoard();
BOARD* board = frame()->GetBoard();
std::vector<BOARD_ITEM*> items;
std::set<KICAD_T> typesRequested, typesInserted;
bool handledAnything = false;
for( const common::types::ItemType& typeMessage : aMsg.types() )
for( int typeRaw : aMsg.types() )
{
KICAD_T type;
auto typeMessage = static_cast<common::types::KiCadObjectType>( typeRaw );
KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
if( std::optional<KICAD_T> opt_type = magic_enum::enum_cast<KICAD_T>( typeMessage.type() ) )
type = *opt_type;
else
if( type == TYPE_NOT_INIT )
continue;
typesRequested.emplace( type );
@ -313,6 +392,31 @@ HANDLER_RESULT<GetItemsResponse> API_HANDLER_PCB::handleGetItems( GetItems& aMsg
typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
break;
case PCB_PAD_T:
{
handledAnything = true;
for( FOOTPRINT* fp : board->Footprints() )
{
std::copy( fp->Pads().begin(), fp->Pads().end(),
std::back_inserter( items ) );
}
typesInserted.insert( PCB_PAD_T );
break;
}
case PCB_FOOTPRINT_T:
{
handledAnything = true;
std::copy( board->Footprints().begin(), board->Footprints().end(),
std::back_inserter( items ) );
typesInserted.insert( PCB_FOOTPRINT_T );
break;
}
default:
break;
}
@ -341,154 +445,259 @@ HANDLER_RESULT<GetItemsResponse> API_HANDLER_PCB::handleGetItems( GetItems& aMsg
}
HANDLER_RESULT<UpdateItemsResponse> API_HANDLER_PCB::handleUpdateItems( UpdateItems& aMsg )
void API_HANDLER_PCB::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
const HANDLER_CONTEXT& aCtx )
{
ApiResponseStatus e;
if( !validateItemHeaderDocument( aMsg.header() ) )
{
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
BOARD* board = m_frame->GetBoard();
BOARD_ITEM_SET boardItems = board->GetItemSet();
std::map<KIID, BOARD_ITEM*> itemUuidMap;
std::for_each( boardItems.begin(), boardItems.end(),
[&]( BOARD_ITEM* aItem )
{
itemUuidMap[aItem->m_Uuid] = aItem;
} );
BOARD_COMMIT* commit = getCurrentCommit();
UpdateItemsResponse response;
for( const google::protobuf::Any& anyItem : aMsg.items() )
{
ItemUpdateResult itemResult;
std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
if( !type )
{
itemResult.set_status( ItemUpdateStatus::IUS_INVALID_TYPE );
response.mutable_updated_items()->Add( std::move( itemResult ) );
continue;
}
std::unique_ptr<BOARD_ITEM> temporaryItem = createItemForType( *type, board );
if( !temporaryItem )
{
itemResult.set_status( ItemUpdateStatus::IUS_INVALID_TYPE );
response.mutable_updated_items()->Add( std::move( itemResult ) );
continue;
}
if( !temporaryItem->Deserialize( anyItem ) )
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "could not unpack {} from request",
magic_enum::enum_name( *type ) ) );
return tl::unexpected( e );
}
if( !itemUuidMap.count( temporaryItem->m_Uuid ) )
{
itemResult.set_status( ItemUpdateStatus::IUS_NONEXISTENT );
response.mutable_updated_items()->Add( std::move( itemResult ) );
continue;
}
BOARD_ITEM* boardItem = itemUuidMap[temporaryItem->m_Uuid];
boardItem->SwapItemData( temporaryItem.get() );
itemResult.set_status( ItemUpdateStatus::IUS_OK );
boardItem->Serialize( *itemResult.mutable_item() );
commit->Modify( boardItem );
itemResult.set_status( ItemUpdateStatus::IUS_OK );
response.mutable_updated_items()->Add( std::move( itemResult ) );
}
response.set_status( ItemRequestStatus::IRS_OK );
pushCurrentCommit( "Updated items via API" );
return response;
}
HANDLER_RESULT<DeleteItemsResponse> API_HANDLER_PCB::handleDeleteItems( DeleteItems& aMsg )
{
if( !validateItemHeaderDocument( aMsg.header() ) )
{
ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
std::map<KIID, ItemDeletionStatus> itemsToDelete;
for( const common::types::KIID& kiidBuf : aMsg.item_ids() )
{
if( !kiidBuf.value().empty() )
{
KIID kiid( kiidBuf.value() );
itemsToDelete[kiid] = ItemDeletionStatus::IDS_NONEXISTENT;
}
}
if( itemsToDelete.empty() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "no valid items to delete were given" );
return tl::unexpected( e );
}
BOARD* board = m_frame->GetBoard();
// This is somewhat inefficient on paper, but the total number of items on a board is
// not computationally-speaking very high even on what we'd consider a large design.
// If this ends up not being the case, we should consider doing something like refactoring
// BOARD to contain all items in a contiguous memory arena and constructing views over it
// when we want to filter to just tracks, etc.
BOARD_ITEM_SET items = board->GetItemSet();
BOARD* board = frame()->GetBoard();
std::vector<BOARD_ITEM*> validatedItems;
for( BOARD_ITEM* item : items )
for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
{
if( itemsToDelete.count( item->m_Uuid ) )
if( BOARD_ITEM* item = board->GetItem( pair.first ) )
{
validatedItems.push_back( item );
itemsToDelete[item->m_Uuid] = ItemDeletionStatus::IDS_OK;
aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
}
// Note: we don't currently support locking items from API modification, but here is where
// to add it in the future (and return IDS_IMMUTABLE)
}
BOARD_COMMIT* commit = getCurrentCommit();
COMMIT* commit = getCurrentCommit( aCtx );
for( BOARD_ITEM* item : validatedItems )
commit->Remove( item );
if( !m_transactionInProgress )
pushCurrentCommit( "Deleted items via API" );
if( !m_activeClients.count( aCtx.ClientName ) )
pushCurrentCommit( aCtx, _( "Deleted items via API" ) );
}
DeleteItemsResponse response;
for( const auto& [id, status] : itemsToDelete )
std::optional<EDA_ITEM*> API_HANDLER_PCB::getItemFromDocument( const DocumentSpecifier& aDocument,
const KIID& aId )
{
if( !validateDocument( aDocument ) )
return std::nullopt;
return getItemById( aId );
}
HANDLER_RESULT<BoardStackupResponse> API_HANDLER_PCB::handleGetStackup( GetBoardStackup& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
if( !documentValidation )
return tl::unexpected( documentValidation.error() );
const BOARD* board = frame()->GetBoard();
BoardStackupResponse response;
google::protobuf::Any any;
const BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
if( frame()->GetBoard()->GetDesignSettings().m_HasStackup )
{
ItemDeletionResult result;
result.mutable_id()->set_value( id.AsStdString() );
result.set_status( status );
const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
stackup.Serialize( any );
}
else
{
BOARD_STACKUP stackup;
stackup.BuildDefaultStackupList( &bds, board->GetCopperLayerCount() );
stackup.Serialize( any );
}
response.set_status( ItemRequestStatus::IRS_OK );
any.UnpackTo( response.mutable_stackup() );
return response;
}
HANDLER_RESULT<GraphicsDefaultsResponse> API_HANDLER_PCB::handleGetGraphicsDefaults(
GetGraphicsDefaults& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
if( !documentValidation )
return tl::unexpected( documentValidation.error() );
const BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings();
GraphicsDefaultsResponse response;
// TODO: This should change to be an enum class
constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
kiapi::board::BLC_SILKSCREEN,
kiapi::board::BLC_COPPER,
kiapi::board::BLC_EDGES,
kiapi::board::BLC_COURTYARD,
kiapi::board::BLC_FABRICATION,
kiapi::board::BLC_OTHER
};
for( int i = 0; i < LAYER_CLASS_COUNT; ++i )
{
kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
l->set_layer( classOrder[i] );
l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] );
kiapi::common::types::TextAttributes* text = l->mutable_text();
text->mutable_size()->set_x_nm( bds.m_TextSize[i].x );
text->mutable_size()->set_y_nm( bds.m_TextSize[i].y );
text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] );
text->set_italic( bds.m_TextItalic[i] );
text->set_keep_upright( bds.m_TextUpright[i] );
}
return response;
}
HANDLER_RESULT<commands::BoundingBoxResponse> API_HANDLER_PCB::handleGetTextExtents(
GetTextExtents& aMsg,
const HANDLER_CONTEXT& aCtx )
{
PCB_TEXT text( frame()->GetBoard() );
google::protobuf::Any any;
any.PackFrom( aMsg.text() );
if( !text.Deserialize( any ) )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "Could not decode text in GetTextExtents message" );
return tl::unexpected( e );
}
commands::BoundingBoxResponse response;
BOX2I bbox = text.GetTextBox();
EDA_ANGLE angle = text.GetTextAngle();
if( !angle.IsZero() )
bbox = bbox.GetBoundingBoxRotated( text.GetTextPos(), text.GetTextAngle() );
response.mutable_position()->set_x_nm( bbox.GetPosition().x );
response.mutable_position()->set_y_nm( bbox.GetPosition().y );
response.mutable_size()->set_x_nm( bbox.GetSize().x );
response.mutable_size()->set_y_nm( bbox.GetSize().y );
return response;
}
HANDLER_RESULT<Empty> API_HANDLER_PCB::handleInteractiveMoveItems( InteractiveMoveItems& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
if( !documentValidation )
return tl::unexpected( documentValidation.error() );
TOOL_MANAGER* mgr = frame()->GetToolManager();
std::vector<EDA_ITEM*> toSelect;
for( const kiapi::common::types::KIID& id : aMsg.items() )
{
if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
toSelect.emplace_back( static_cast<EDA_ITEM*>( *item ) );
}
if( toSelect.empty() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "None of the given items exist on the board",
aMsg.board().board_filename() ) );
return tl::unexpected( e );
}
PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() );
mgr->RunAction( PCB_ACTIONS::selectionClear );
mgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &toSelect );
COMMIT* commit = getCurrentCommit( aCtx );
mgr->PostAction( PCB_ACTIONS::move, commit );
return Empty();
}
HANDLER_RESULT<NetsResponse> API_HANDLER_PCB::handleGetNets( GetNets& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
if( !documentValidation )
return tl::unexpected( documentValidation.error() );
NetsResponse response;
BOARD* board = frame()->GetBoard();
std::set<wxString> netclassFilter;
for( const std::string& nc : aMsg.netclass_filter() )
netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
for( NETINFO_ITEM* net : board->GetNetInfo() )
{
NETCLASS* nc = net->GetNetClass();
if( !netclassFilter.empty() && nc && !netclassFilter.count( nc->GetName() ) )
continue;
board::types::Net* netProto = response.add_nets();
netProto->set_name( net->GetNetname() );
netProto->mutable_code()->set_value( net->GetNetCode() );
}
return response;
}
HANDLER_RESULT<Empty> API_HANDLER_PCB::handleRefillZones( RefillZones& aMsg,
const HANDLER_CONTEXT& aCtx )
{
if( std::optional<ApiResponseStatus> busy = checkForBusy() )
return tl::unexpected( *busy );
HANDLER_RESULT<bool> documentValidation = validateDocument( aMsg.board() );
if( !documentValidation )
return tl::unexpected( documentValidation.error() );
if( aMsg.zones().empty() )
{
TOOL_MANAGER* mgr = frame()->GetToolManager();
frame()->CallAfter( [mgr]()
{
mgr->RunAction( PCB_ACTIONS::zoneFillAll );
} );
}
else
{
// TODO
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
return tl::unexpected( e );
}
return Empty();
}

View File

@ -23,14 +23,16 @@
#include <google/protobuf/empty.pb.h>
#include <api/api_handler.h>
#include <api/api_handler_editor.h>
#include <api/board/board_commands.pb.h>
#include <api/board/board_types.pb.h>
#include <api/common/commands/editor_commands.pb.h>
#include <kiid.h>
#include <properties/property_mgr.h>
using namespace kiapi;
using namespace kiapi::common;
using namespace kiapi::board::commands;
using google::protobuf::Empty;
@ -44,7 +46,7 @@ class PCB_TRACK;
class PROPERTY_BASE;
class API_HANDLER_PCB : public API_HANDLER
class API_HANDLER_PCB : public API_HANDLER_EDITOR
{
public:
API_HANDLER_PCB( PCB_EDIT_FRAME* aFrame );
@ -52,35 +54,64 @@ public:
private:
typedef std::map<std::string, PROPERTY_BASE*> PROTO_PROPERTY_MAP;
static std::unique_ptr<BOARD_ITEM> createItemForType( KICAD_T aType,
static HANDLER_RESULT<std::unique_ptr<BOARD_ITEM>> createItemForType( KICAD_T aType,
BOARD_ITEM_CONTAINER* aContainer );
HANDLER_RESULT<commands::RunActionResponse> handleRunAction( commands::RunAction& aMsg );
HANDLER_RESULT<commands::RunActionResponse> handleRunAction( commands::RunAction& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::GetOpenDocumentsResponse> handleGetOpenDocuments(
commands::GetOpenDocuments& aMsg );
commands::GetOpenDocuments& aMsg, const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::BeginCommitResponse> handleBeginCommit( commands::BeginCommit& aMsg );
HANDLER_RESULT<commands::EndCommitResponse> handleEndCommit( commands::EndCommit& aMsg );
HANDLER_RESULT<commands::GetItemsResponse> handleGetItems( commands::GetItems& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::CreateItemsResponse> handleCreateItems( commands::CreateItems& aMsg );
HANDLER_RESULT<commands::GetItemsResponse> handleGetItems( commands::GetItems& aMsg );
HANDLER_RESULT<commands::UpdateItemsResponse> handleUpdateItems( commands::UpdateItems& aMsg );
HANDLER_RESULT<commands::DeleteItemsResponse> handleDeleteItems( commands::DeleteItems& aMsg );
HANDLER_RESULT<BoardStackupResponse> handleGetStackup( GetBoardStackup& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<GraphicsDefaultsResponse> handleGetGraphicsDefaults( GetGraphicsDefaults& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<commands::BoundingBoxResponse> handleGetTextExtents( GetTextExtents& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<Empty> handleInteractiveMoveItems( InteractiveMoveItems& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<NetsResponse> handleGetNets( GetNets& aMsg,
const HANDLER_CONTEXT& aCtx );
HANDLER_RESULT<Empty> handleRefillZones( RefillZones& aMsg, const HANDLER_CONTEXT& aCtx );
protected:
std::unique_ptr<COMMIT> createCommit() override;
kiapi::common::types::DocumentType thisDocumentType() const override
{
return kiapi::common::types::DOCTYPE_PCB;
}
bool validateDocumentInternal( const DocumentSpecifier& aDocument ) const override;
void deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
const HANDLER_CONTEXT& aCtx ) override;
std::optional<EDA_ITEM*> getItemFromDocument( const DocumentSpecifier& aDocument,
const KIID& aId ) override;
private:
PCB_EDIT_FRAME* frame() const;
bool validateItemHeaderDocument( const common::types::ItemHeader& aHeader );
void pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage ) override;
BOARD_COMMIT* getCurrentCommit();
std::optional<BOARD_ITEM*> getItemById( const KIID& aId ) const;
void pushCurrentCommit( const std::string& aMessage );
PCB_EDIT_FRAME* m_frame;
std::unique_ptr<BOARD_COMMIT> m_commit;
bool m_transactionInProgress;
HANDLER_RESULT<types::ItemRequestStatus> handleCreateUpdateItemsInternal( bool aCreate,
const HANDLER_CONTEXT& aCtx,
const types::ItemHeader &aHeader,
const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
std::function<void(commands::ItemStatus, google::protobuf::Any)> aItemHandler )
override;
};
#endif //KICAD_API_HANDLER_PCB_H

View File

@ -0,0 +1,105 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <import_export.h>
#include <api/api_enums.h>
#include <api/board/board_types.pb.h>
#include <wx/wx.h>
#include <pad_shapes.h>
#include <zones.h>
using namespace kiapi::board;
template<>
types::PadStackShape ToProtoEnum( PAD_SHAPE aValue )
{
switch( aValue )
{
case PAD_SHAPE::CIRCLE: return types::PadStackShape::PSS_CIRCLE;
case PAD_SHAPE::RECTANGLE: return types::PadStackShape::PSS_RECTANGLE;
case PAD_SHAPE::OVAL: return types::PadStackShape::PSS_OVAL;
case PAD_SHAPE::TRAPEZOID: return types::PadStackShape::PSS_TRAPEZOID;
case PAD_SHAPE::ROUNDRECT: return types::PadStackShape::PSS_ROUNDRECT;
case PAD_SHAPE::CHAMFERED_RECT: return types::PadStackShape::PSS_CHAMFEREDRECT;
case PAD_SHAPE::CUSTOM: return types::PadStackShape::PSS_CUSTOM;
default:
wxCHECK_MSG( false, types::PadStackShape::PSS_UNKNOWN,
"Unhandled case in ToProtoEnum<PAD_SHAPE>");
}
}
template<>
PAD_SHAPE FromProtoEnum( types::PadStackShape aValue )
{
switch( aValue )
{
case types::PadStackShape::PSS_CIRCLE: return PAD_SHAPE::CIRCLE;
case types::PadStackShape::PSS_RECTANGLE: return PAD_SHAPE::RECTANGLE;
case types::PadStackShape::PSS_OVAL: return PAD_SHAPE::OVAL;
case types::PadStackShape::PSS_TRAPEZOID: return PAD_SHAPE::TRAPEZOID;
case types::PadStackShape::PSS_ROUNDRECT: return PAD_SHAPE::ROUNDRECT;
case types::PadStackShape::PSS_CHAMFEREDRECT: return PAD_SHAPE::CHAMFERED_RECT;
case types::PadStackShape::PSS_CUSTOM: return PAD_SHAPE::CUSTOM;
default:
wxCHECK_MSG( false, PAD_SHAPE::CIRCLE,
"Unhandled case in FromProtoEnum<types::PadStackShape>" );
}
}
template<>
types::ZoneConnectionStyle ToProtoEnum( ZONE_CONNECTION aValue )
{
switch( aValue )
{
case ZONE_CONNECTION::INHERITED: return types::ZoneConnectionStyle::ZCS_INHERITED;
case ZONE_CONNECTION::NONE: return types::ZoneConnectionStyle::ZCS_NONE;
case ZONE_CONNECTION::THERMAL: return types::ZoneConnectionStyle::ZCS_THERMAL;
case ZONE_CONNECTION::FULL: return types::ZoneConnectionStyle::ZCS_FULL;
case ZONE_CONNECTION::THT_THERMAL: return types::ZoneConnectionStyle::ZCS_PTH_THERMAL;
default:
wxCHECK_MSG( false, types::ZoneConnectionStyle::ZCS_UNKNOWN,
"Unhandled case in ToProtoEnum<ZONE_CONNECTION>");
}
}
template<>
ZONE_CONNECTION FromProtoEnum( types::ZoneConnectionStyle aValue )
{
switch( aValue )
{
case types::ZoneConnectionStyle::ZCS_UNKNOWN: return ZONE_CONNECTION::INHERITED;
case types::ZoneConnectionStyle::ZCS_INHERITED: return ZONE_CONNECTION::INHERITED;
case types::ZoneConnectionStyle::ZCS_NONE: return ZONE_CONNECTION::NONE;
case types::ZoneConnectionStyle::ZCS_THERMAL: return ZONE_CONNECTION::THERMAL;
case types::ZoneConnectionStyle::ZCS_FULL: return ZONE_CONNECTION::FULL;
case types::ZoneConnectionStyle::ZCS_PTH_THERMAL: return ZONE_CONNECTION::THT_THERMAL;
default:
wxCHECK_MSG( false, ZONE_CONNECTION::INHERITED,
"Unhandled case in FromProtoEnum<types::ZoneConnectionStyle>" );
}
}

View File

@ -0,0 +1,100 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <api/api_pcb_utils.h>
#include <api/api_enums.h>
#include <board.h>
#include <board_item_container.h>
#include <footprint.h>
#include <pad.h>
#include <pcb_group.h>
#include <pcb_reference_image.h>
#include <pcb_shape.h>
#include <pcb_track.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <zone.h>
std::unique_ptr<BOARD_ITEM> CreateItemForType( KICAD_T aType, BOARD_ITEM_CONTAINER* aContainer )
{
switch( aType )
{
case PCB_TRACE_T: return std::make_unique<PCB_TRACK>( aContainer );
case PCB_ARC_T: return std::make_unique<PCB_ARC>( aContainer );
case PCB_VIA_T: return std::make_unique<PCB_VIA>( aContainer );
case PCB_TEXT_T: return std::make_unique<PCB_TEXT>( aContainer );
case PCB_TEXTBOX_T: return std::make_unique<PCB_TEXTBOX>( aContainer );
case PCB_SHAPE_T: return std::make_unique<PCB_SHAPE>( aContainer );
case PCB_ZONE_T: return std::make_unique<ZONE>( aContainer );
case PCB_GROUP_T: return std::make_unique<PCB_GROUP>( aContainer );
case PCB_REFERENCE_IMAGE_T: return std::make_unique<PCB_REFERENCE_IMAGE>( aContainer );
case PCB_PAD_T:
{
FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aContainer );
if( !footprint )
return nullptr;
return std::make_unique<PAD>( footprint );
}
case PCB_FOOTPRINT_T:
{
BOARD* board = dynamic_cast<BOARD*>( aContainer );
if( !board )
return nullptr;
return std::make_unique<FOOTPRINT>( board );
}
default:
return nullptr;
}
}
namespace kiapi::board
{
void PackLayerSet( google::protobuf::RepeatedField<int>& aOutput, const LSET& aLayerSet )
{
for( const PCB_LAYER_ID& layer : aLayerSet.Seq() )
aOutput.Add( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
}
LSET UnpackLayerSet( const google::protobuf::RepeatedField<int>& aProtoLayerSet )
{
LSET set;
for( int layer : aProtoLayerSet )
{
wxCHECK2( layer >= F_Cu && layer < PCB_LAYER_ID_COUNT, continue );
PCB_LAYER_ID boardLayer =
FromProtoEnum<PCB_LAYER_ID>( static_cast<types::BoardLayer>( layer ) );
set.set( boardLayer );
}
return set;
}
} // namespace kiapi::board

View File

@ -0,0 +1,45 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_PCB_UTLIS_H
#define KICAD_API_PCB_UTLIS_H
#include <memory>
#include <core/typeinfo.h>
#include <import_export.h>
#include <layer_ids.h>
#include <api/common/types/base_types.pb.h>
#include <api/board/board_types.pb.h>
class BOARD_ITEM;
class BOARD_ITEM_CONTAINER;
std::unique_ptr<BOARD_ITEM> CreateItemForType( KICAD_T aType, BOARD_ITEM_CONTAINER* aContainer );
namespace kiapi::board
{
void PackLayerSet( google::protobuf::RepeatedField<int>& aOutput, const LSET& aLayerSet );
LSET UnpackLayerSet( const google::protobuf::RepeatedField<int>& aInput );
} // namespace kiapi::board
#endif //KICAD_API_PCB_UTLIS_H

View File

@ -154,37 +154,26 @@ BOARD::~BOARD()
// Clean up the owned elements
DeleteMARKERs();
for( ZONE* zone : m_zones )
delete zone;
m_zones.clear();
delete m_SolderMaskBridges;
for( FOOTPRINT* footprint : m_footprints )
delete footprint;
BOARD_ITEM_SET ownedItems = GetItemSet();
m_zones.clear();
m_footprints.clear();
for( PCB_TRACK* t : m_tracks )
delete t;
m_tracks.clear();
for( BOARD_ITEM* d : m_drawings )
delete d;
m_drawings.clear();
for( PCB_GROUP* g : m_groups )
delete g;
m_groups.clear();
// Generators not currently returned by GetItemSet
for( PCB_GENERATOR* g : m_generators )
delete g;
ownedItems.insert( g );
m_generators.clear();
// Delete the owned items after clearing the containers, because some item dtors
// cause call chains that query the containers
for( BOARD_ITEM* item : ownedItems )
delete item;
}

View File

@ -27,6 +27,9 @@
#include <board.h>
#include <i18n_utility.h> // For _HKI definition
#include "stackup_predefined_prms.h"
#include <google/protobuf/any.pb.h>
#include <api/board/board.pb.h>
#include <api/api_enums.h>
bool DIELECTRIC_PRMS::operator==( const DIELECTRIC_PRMS& aOther ) const
@ -418,6 +421,44 @@ bool BOARD_STACKUP::operator==( const BOARD_STACKUP& aOther ) const
}
void BOARD_STACKUP::Serialize( google::protobuf::Any& aContainer ) const
{
kiapi::board::BoardStackup stackup;
for( const BOARD_STACKUP_ITEM* item : m_list )
{
kiapi::board::BoardStackupLayer* layer = stackup.mutable_layers()->Add();
// TODO dielectric sub-layers
layer->mutable_thickness()->set_value_nm( item->GetThickness() );
layer->set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
item->GetBrdLayerId() ) );
switch( item->GetType() )
{
case BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_COPPER:
{
layer->mutable_copper()->New();
// (no copper params yet...)
break;
}
default:
// TODO
break;
}
}
aContainer.PackFrom( stackup );
}
bool BOARD_STACKUP::Deserialize( const google::protobuf::Any& aContainer )
{
return true;
}
void BOARD_STACKUP::RemoveAll()
{
for( BOARD_STACKUP_ITEM* item : m_list )

View File

@ -29,6 +29,7 @@
#include <vector>
#include <wx/string.h>
#include <layer_ids.h>
#include <api/serializable.h>
class BOARD;
class BOARD_DESIGN_SETTINGS;
@ -213,7 +214,7 @@ private:
* @note There are a few other parameters related to the physical stackup like finish type,
* impedance control and a few others.
*/
class BOARD_STACKUP
class BOARD_STACKUP : public SERIALIZABLE
{
public:
BOARD_STACKUP();
@ -225,6 +226,10 @@ public:
~BOARD_STACKUP() { RemoveAll(); }
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
const std::vector<BOARD_STACKUP_ITEM*>& GetList() const { return m_list; }
/// @return a reference to the layer aIndex, or nullptr if not exists

View File

@ -719,7 +719,7 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
}
case MAIL_RELOAD_PLUGINS:
GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload );
GetToolManager()->RunAction( ACTIONS::pluginsReload );
break;
// many many others.

View File

@ -23,6 +23,7 @@
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <magic_enum.hpp>
#include <core/mirror.h>
#include <confirm.h>
@ -53,6 +54,12 @@
#include <geometry/convex_hull.h>
#include "convert_basic_shapes_to_polygon.h"
#include <google/protobuf/any.pb.h>
#include <api/board/board_types.pb.h>
#include <api/api_enums.h>
#include <api/api_utils.h>
#include <api/api_pcb_utils.h>
FOOTPRINT::FOOTPRINT( BOARD* parent ) :
BOARD_ITEM_CONTAINER((BOARD_ITEM*) parent, PCB_FOOTPRINT_T ),
@ -253,6 +260,248 @@ FOOTPRINT::~FOOTPRINT()
}
void FOOTPRINT::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::FootprintInstance footprint;
footprint.mutable_id()->set_value( m_Uuid.AsStdString() );
footprint.mutable_position()->set_x_nm( GetPosition().x );
footprint.mutable_position()->set_y_nm( GetPosition().y );
footprint.mutable_orientation()->set_value_degrees( GetOrientationDegrees() );
footprint.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
footprint.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
google::protobuf::Any buf;
GetField( REFERENCE_FIELD )->Serialize( buf );
buf.UnpackTo( footprint.mutable_reference_field() );
GetField( VALUE_FIELD )->Serialize( buf );
buf.UnpackTo( footprint.mutable_value_field() );
GetField( DATASHEET_FIELD )->Serialize( buf );
buf.UnpackTo( footprint.mutable_datasheet_field() );
GetField( DESCRIPTION_FIELD )->Serialize( buf );
buf.UnpackTo( footprint.mutable_description_field() );
kiapi::board::types::FootprintAttributes* attrs = footprint.mutable_attributes();
attrs->set_not_in_schematic( IsBoardOnly() );
attrs->set_exclude_from_position_files( IsExcludedFromPosFiles() );
attrs->set_exclude_from_bill_of_materials( IsExcludedFromBOM() );
attrs->set_exempt_from_courtyard_requirement( AllowMissingCourtyard() );
attrs->set_do_not_populate( IsDNP() );
kiapi::board::types::Footprint* def = footprint.mutable_definition();
def->mutable_id()->CopyFrom( kiapi::common::LibIdToProto( GetFPID() ) );
// anchor?
def->mutable_attributes()->set_description( GetLibDescription().ToStdString() );
def->mutable_attributes()->set_keywords( GetKeywords().ToStdString() );
// TODO: serialize library mandatory fields
kiapi::board::types::DesignRuleOverrides* overrides = def->mutable_overrides();
if( GetLocalClearance().has_value() )
overrides->mutable_clearance()->set_value_nm( *GetLocalClearance() );
if( GetLocalSolderMaskMargin().has_value() )
overrides->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() );
if( GetLocalSolderPasteMargin().has_value() )
overrides->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() );
if( GetLocalSolderPasteMarginRatio().has_value() )
overrides->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() );
overrides->set_zone_connection(
ToProtoEnum<ZONE_CONNECTION,
kiapi::board::types::ZoneConnectionStyle>( GetLocalZoneConnection() ) );
for( const wxString& group : GetNetTiePadGroups() )
{
kiapi::board::types::NetTieDefinition* netTie = def->add_net_ties();
wxStringTokenizer tokenizer( group, " " );
while( tokenizer.HasMoreTokens() )
netTie->add_pad_number( tokenizer.GetNextToken().ToStdString() );
}
for( PCB_LAYER_ID layer : GetPrivateLayers().Seq() )
{
def->add_private_layers(
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( layer ) );
}
for( const PCB_FIELD* item : Fields() )
{
if( item->GetId() < MANDATORY_FIELDS )
continue;
google::protobuf::Any* itemMsg = def->add_items();
item->Serialize( *itemMsg );
}
for( const PAD* item : Pads() )
{
google::protobuf::Any* itemMsg = def->add_items();
item->Serialize( *itemMsg );
}
for( const BOARD_ITEM* item : GraphicalItems() )
{
google::protobuf::Any* itemMsg = def->add_items();
item->Serialize( *itemMsg );
}
for( const ZONE* item : Zones() )
{
google::protobuf::Any* itemMsg = def->add_items();
item->Serialize( *itemMsg );
}
// TODO: 3D models
aContainer.PackFrom( footprint );
}
bool FOOTPRINT::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::FootprintInstance footprint;
if( !aContainer.UnpackTo( &footprint ) )
return false;
const_cast<KIID&>( m_Uuid ) = KIID( footprint.id().value() );
SetPosition( VECTOR2I( footprint.position().x_nm(), footprint.position().y_nm() ) );
SetOrientationDegrees( footprint.orientation().value_degrees() );
SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( footprint.layer() ) );
SetLocked( footprint.locked() == kiapi::common::types::LockedState::LS_LOCKED );
google::protobuf::Any buf;
kiapi::board::types::Field mandatoryField;
if( footprint.has_reference_field() )
{
mandatoryField = footprint.reference_field();
mandatoryField.mutable_id()->set_id( REFERENCE_FIELD );
buf.PackFrom( mandatoryField );
GetField( REFERENCE_FIELD )->Deserialize( buf );
}
if( footprint.has_value_field() )
{
mandatoryField = footprint.value_field();
mandatoryField.mutable_id()->set_id( VALUE_FIELD );
buf.PackFrom( mandatoryField );
GetField( VALUE_FIELD )->Deserialize( buf );
}
if( footprint.has_datasheet_field() )
{
mandatoryField = footprint.datasheet_field();
mandatoryField.mutable_id()->set_id( DATASHEET_FIELD );
buf.PackFrom( mandatoryField );
GetField( DATASHEET_FIELD )->Deserialize( buf );
}
if( footprint.has_description_field() )
{
mandatoryField = footprint.description_field();
mandatoryField.mutable_id()->set_id( DESCRIPTION_FIELD );
buf.PackFrom( mandatoryField );
GetField( DESCRIPTION_FIELD )->Deserialize( buf );
}
SetBoardOnly( footprint.attributes().not_in_schematic() );
SetExcludedFromBOM( footprint.attributes().exclude_from_bill_of_materials() );
SetExcludedFromPosFiles( footprint.attributes().exclude_from_position_files() );
SetAllowMissingCourtyard( footprint.attributes().exempt_from_courtyard_requirement() );
SetDNP( footprint.attributes().do_not_populate() );
// Definition
SetFPID( kiapi::common::LibIdFromProto( footprint.definition().id() ) );
// TODO: how should anchor be handled?
SetLibDescription( footprint.definition().attributes().description() );
SetKeywords( footprint.definition().attributes().keywords() );
const kiapi::board::types::DesignRuleOverrides& overrides = footprint.overrides();
if( overrides.has_clearance() )
SetLocalClearance( overrides.clearance().value_nm() );
else
SetLocalClearance( std::nullopt );
if( overrides.has_solder_mask_margin() )
SetLocalSolderMaskMargin( overrides.solder_mask_margin().value_nm() );
else
SetLocalSolderMaskMargin( std::nullopt );
if( overrides.has_solder_paste_margin() )
SetLocalSolderPasteMargin( overrides.solder_paste_margin().value_nm() );
else
SetLocalSolderPasteMargin( std::nullopt );
if( overrides.has_solder_paste_margin_ratio() )
SetLocalSolderPasteMarginRatio( overrides.solder_paste_margin_ratio().value() );
else
SetLocalSolderPasteMarginRatio( std::nullopt );
SetLocalZoneConnection( FromProtoEnum<ZONE_CONNECTION>( overrides.zone_connection() ) );
for( const kiapi::board::types::NetTieDefinition& netTieMsg : footprint.definition().net_ties() )
{
wxString group;
for( const std::string& pad : netTieMsg.pad_number() )
group.Append( wxString::Format( wxT( "%s " ), pad ) );
group.Trim();
AddNetTiePadGroup( group );
}
LSET privateLayers;
for( int layerMsg : footprint.definition().private_layers() )
{
auto layer = static_cast<kiapi::board::types::BoardLayer>( layerMsg );
privateLayers.set( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( layer ) );
}
SetPrivateLayers( privateLayers );
// Footprint items
for( PCB_FIELD* field : Fields() )
{
if( field->GetId() >= MANDATORY_FIELDS )
Remove( field );
}
Pads().clear();
GraphicalItems().clear();
Zones().clear();
Groups().clear();
Models().clear();
for( const google::protobuf::Any& itemMsg : footprint.definition().items() )
{
std::optional<KICAD_T> type = kiapi::common::TypeNameFromAny( itemMsg );
if( !type )
continue;
std::unique_ptr<BOARD_ITEM> item = CreateItemForType( *type, this );
if( item && item->Deserialize( itemMsg ) )
Add( item.release(), ADD_MODE::APPEND );
}
// TODO: 3D models
return true;
}
PCB_FIELD* FOOTPRINT::GetField( MANDATORY_FIELD_T aFieldType )
{
return m_fields[aFieldType];

View File

@ -117,6 +117,9 @@ public:
FOOTPRINT& operator=( const FOOTPRINT& aOther );
FOOTPRINT& operator=( FOOTPRINT&& aOther );
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && aItem->Type() == PCB_FOOTPRINT_T;

View File

@ -1151,9 +1151,9 @@ void FOOTPRINT_EDIT_FRAME::setupTools()
PCB_EDIT_FRAME* pcbframe = static_cast<PCB_EDIT_FRAME*>( Kiway().Player( FRAME_PCB_EDITOR, false ) );
if( pcbframe )
pcbframe->GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload );
pcbframe->GetToolManager()->RunAction( ACTIONS::pluginsReload );
else
m_toolManager->RunAction( PCB_ACTIONS::pluginsReload );
m_toolManager->RunAction( ACTIONS::pluginsReload );
}

View File

@ -643,7 +643,7 @@ void FOOTPRINT_WIZARD_FRAME::PythonPluginsReload()
PCB_EDIT_FRAME* pcbframe = static_cast<PCB_EDIT_FRAME*>( Kiway().Player( FRAME_PCB_EDITOR, false ) );
if( pcbframe )
pcbframe->GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload );
pcbframe->GetToolManager()->RunAction( ACTIONS::pluginsReload );
else
GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload );
GetToolManager()->RunAction( ACTIONS::pluginsReload );
}

View File

@ -436,7 +436,7 @@ void PCB_EDIT_FRAME::doReCreateMenuBar()
submenuActionPlugins->SetTitle( _( "External Plugins" ) );
submenuActionPlugins->SetIcon( BITMAPS::puzzle_piece );
submenuActionPlugins->Add( PCB_ACTIONS::pluginsReload );
submenuActionPlugins->Add( ACTIONS::pluginsReload );
submenuActionPlugins->Add( PCB_ACTIONS::pluginsShowFolder );
// Populate the Action Plugin sub-menu: Must be done before Add

View File

@ -50,6 +50,10 @@
#include <pcb_painter.h>
#include <properties/property_validators.h>
#include <wx/log.h>
#include <api/api_enums.h>
#include <api/api_utils.h>
#include <api/api_pcb_utils.h>
#include <api/board/board_types.pb.h>
#include <memory>
#include <macros.h>
@ -135,6 +139,160 @@ PAD& PAD::operator=( const PAD &aOther )
}
void PAD::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::Pad pad;
pad.mutable_id()->set_value( m_Uuid.AsStdString() );
kiapi::common::PackVector2( *pad.mutable_position(), GetPosition() );
pad.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
pad.mutable_net()->mutable_code()->set_value( GetNetCode() );
pad.mutable_net()->set_name( GetNetname() );
kiapi::board::types::PadStack* padstack = pad.mutable_pad_stack();
padstack->set_type( kiapi::board::types::PadStackType::PST_THROUGH );
padstack->set_start_layer(
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( m_layer ) );
padstack->set_end_layer(
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( FlipLayer( m_layer ) ) );
kiapi::common::PackVector2( *padstack->mutable_drill_diameter(),
{ GetDrillSizeX(), GetDrillSizeY() } );
padstack->mutable_angle()->set_value_degrees( GetOrientationDegrees() );
kiapi::board::types::PadStackLayer* stackLayer = padstack->add_layers();
kiapi::board::PackLayerSet( *stackLayer->mutable_layers(), GetLayerSet() );
kiapi::common::PackVector2( *stackLayer->mutable_size(),
{ GetSizeX(), GetSizeY() } );
stackLayer->set_shape(
ToProtoEnum<PAD_SHAPE, kiapi::board::types::PadStackShape>( GetShape() ) );
kiapi::board::types::UnconnectedLayerRemoval ulr;
if( m_removeUnconnectedLayer )
{
if( m_keepTopBottomLayer )
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END;
else
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE;
}
else
{
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP;
}
padstack->set_unconnected_layer_removal( ulr );
kiapi::board::types::DesignRuleOverrides* overrides = pad.mutable_overrides();
if( GetLocalClearance().has_value() )
overrides->mutable_clearance()->set_value_nm( *GetLocalClearance() );
if( GetLocalSolderMaskMargin().has_value() )
overrides->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() );
if( GetLocalSolderPasteMargin().has_value() )
overrides->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() );
if( GetLocalSolderPasteMarginRatio().has_value() )
overrides->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() );
overrides->set_zone_connection(
ToProtoEnum<ZONE_CONNECTION,
kiapi::board::types::ZoneConnectionStyle>( GetLocalZoneConnection() ) );
kiapi::board::types::ThermalSpokeSettings* thermals = pad.mutable_thermal_spokes();
thermals->set_width( GetThermalSpokeWidth() );
thermals->set_gap( GetThermalGap() );
thermals->mutable_angle()->set_value_degrees( GetThermalSpokeAngleDegrees() );
aContainer.PackFrom( pad );
}
bool PAD::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::Pad pad;
if( !aContainer.UnpackTo( &pad ) )
return false;
const_cast<KIID&>( m_Uuid ) = KIID( pad.id().value() );
SetPosition( kiapi::common::UnpackVector2( pad.position() ) );
SetNetCode( pad.net().code().value() );
SetLocked( pad.locked() == kiapi::common::types::LockedState::LS_LOCKED );
const kiapi::board::types::PadStack& padstack = pad.pad_stack();
SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
padstack.start_layer() ) );
SetDrillSize( kiapi::common::UnpackVector2( padstack.drill_diameter() ) );
SetOrientationDegrees( padstack.angle().value_degrees() );
// We don't yet support complex padstacks
if( padstack.layers_size() == 1 )
{
const kiapi::board::types::PadStackLayer& layer = padstack.layers( 0 );
SetSize( kiapi::common::UnpackVector2( layer.size() ) );
SetLayerSet( kiapi::board::UnpackLayerSet( layer.layers() ) );
SetShape( FromProtoEnum<PAD_SHAPE>( layer.shape() ) );
}
switch( padstack.unconnected_layer_removal() )
{
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE:
m_removeUnconnectedLayer = true;
m_keepTopBottomLayer = false;
break;
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END:
m_removeUnconnectedLayer = true;
m_keepTopBottomLayer = true;
break;
default:
case kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP:
m_removeUnconnectedLayer = false;
m_keepTopBottomLayer = false;
break;
}
const kiapi::board::types::DesignRuleOverrides& overrides = pad.overrides();
if( overrides.has_clearance() )
SetLocalClearance( overrides.clearance().value_nm() );
else
SetLocalClearance( std::nullopt );
if( overrides.has_solder_mask_margin() )
SetLocalSolderMaskMargin( overrides.solder_mask_margin().value_nm() );
else
SetLocalSolderMaskMargin( std::nullopt );
if( overrides.has_solder_paste_margin() )
SetLocalSolderPasteMargin( overrides.solder_paste_margin().value_nm() );
else
SetLocalSolderPasteMargin( std::nullopt );
if( overrides.has_solder_paste_margin_ratio() )
SetLocalSolderPasteMarginRatio( overrides.solder_paste_margin_ratio().value() );
else
SetLocalSolderPasteMarginRatio( std::nullopt );
SetLocalZoneConnection( FromProtoEnum<ZONE_CONNECTION>( overrides.zone_connection() ) );
const kiapi::board::types::ThermalSpokeSettings& thermals = pad.thermal_spokes();
SetThermalGap( thermals.gap() );
SetThermalSpokeWidth( thermals.width() );
SetThermalSpokeAngleDegrees( thermals.angle().value_degrees() );
return true;
}
bool PAD::CanHaveNumber() const
{
// Aperture pads don't get a number

View File

@ -65,6 +65,9 @@ public:
PAD( const PAD& aPad );
PAD& operator=( const PAD &aOther );
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
/*
* Default layers used for pads, according to the pad type.
*

View File

@ -266,6 +266,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
ReCreateVToolbar();
ReCreateOptToolbar();
#ifdef KICAD_IPC_API
wxTheApp->Bind( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED,
[&]( wxCommandEvent& aEvt )
{
@ -273,7 +274,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
ReCreateHToolbar();
aEvt.Skip();
} );
#endif
m_propertiesPanel = new PCB_PROPERTIES_PANEL( this, this );
@ -438,7 +439,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
PythonSyncProjectName();
// Sync action plugins in case they changed since the last time the frame opened
GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload );
GetToolManager()->RunAction( ACTIONS::pluginsReload );
#ifdef KICAD_IPC_API
m_apiHandler = std::make_unique<API_HANDLER_PCB>( this );
@ -2062,24 +2063,6 @@ void PCB_EDIT_FRAME::PythonSyncProjectName()
}
void PCB_EDIT_FRAME::OnApiPluginMenu( wxCommandEvent& aEvent )
{
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
if( mgr.MenuBindings().count( aEvent.GetId() ) )
mgr.InvokeAction( mgr.MenuBindings().at( aEvent.GetId() ) );
}
void PCB_EDIT_FRAME::OnApiPluginButton( wxCommandEvent& aEvent )
{
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
if( mgr.ButtonBindings().count( aEvent.GetId() ) )
mgr.InvokeAction( mgr.ButtonBindings().at( aEvent.GetId() ) );
}
void PCB_EDIT_FRAME::ShowFootprintPropertiesDialog( FOOTPRINT* aFootprint )
{
if( aFootprint == nullptr )
@ -2496,6 +2479,26 @@ void PCB_EDIT_FRAME::ProjectChanged()
}
bool PCB_EDIT_FRAME::CanAcceptApiCommands()
{
// For now, be conservative: Don't allow any API use while the user is changing things
if( GetToolManager()->GetCurrentTool() != GetToolManager()->GetTool<PCB_SELECTION_TOOL>() )
return false;
ZONE_FILLER_TOOL* zoneFillerTool = m_toolManager->GetTool<ZONE_FILLER_TOOL>();
if( zoneFillerTool->IsBusy() )
return false;
ROUTER_TOOL* routerTool = m_toolManager->GetTool<ROUTER_TOOL>();
if( routerTool->RoutingInProgress() )
return false;
return EDA_BASE_FRAME::CanAcceptApiCommands();
}
bool ExportBoardToHyperlynx( BOARD* aBoard, const wxFileName& aPath );

View File

@ -699,6 +699,8 @@ public:
void ProjectChanged() override;
bool CanAcceptApiCommands() override;
wxString GetCurrentFileName() const override;
SELECTION& GetCurrentSelection() override;
@ -760,11 +762,6 @@ protected:
*/
void AddActionPluginTools();
/**
* Append actions from API plugins to the main toolbar
*/
void AddApiPluginTools();
/**
* Execute action plugin's Run() method and updates undo buffer.
*
@ -786,8 +783,7 @@ protected:
*/
void OnActionPluginButton( wxCommandEvent& aEvent );
void OnApiPluginMenu( wxCommandEvent& aEvent );
void OnApiPluginButton( wxCommandEvent& aEvent );
PLUGIN_ACTION_SCOPE PluginActionScope() const override { return PLUGIN_ACTION_SCOPE::PCB; }
/**
* Update the state of the GUI after a new board is loaded or created.

View File

@ -27,6 +27,8 @@
#include <board_design_settings.h>
#include <i18n_utility.h>
#include <pcb_painter.h>
#include <api/board/board_types.pb.h>
PCB_FIELD::PCB_FIELD( FOOTPRINT* aParent, int aFieldId, const wxString& aName ) :
PCB_TEXT( aParent, PCB_FIELD_T ),
@ -44,6 +46,49 @@ PCB_FIELD::PCB_FIELD( const PCB_TEXT& aText, int aFieldId, const wxString& aName
}
void PCB_FIELD::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::Field field;
google::protobuf::Any anyText;
PCB_TEXT::Serialize( anyText );
anyText.UnpackTo( field.mutable_text() );
field.set_name( GetCanonicalName().ToStdString() );
field.mutable_id()->set_id( GetId() );
aContainer.PackFrom( field );
}
bool PCB_FIELD::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::Field field;
if( !aContainer.UnpackTo( &field ) )
return false;
if( field.has_id() )
setId( field.id().id() );
// Mandatory fields have a blank Name in the KiCad object
if( m_id >= MANDATORY_FIELDS )
SetName( wxString( field.name().c_str(), wxConvUTF8 ) );
if( field.has_text() )
{
google::protobuf::Any anyText;
anyText.PackFrom( field.text() );
PCB_TEXT::Deserialize( anyText );
}
if( field.text().layer() == kiapi::board::types::BoardLayer::BL_UNKNOWN )
SetLayer( F_SilkS );
return true;
}
wxString PCB_FIELD::GetName( bool aUseDefaultName ) const
{
if( m_parent && m_parent->Type() == PCB_FOOTPRINT_T )

View File

@ -37,6 +37,9 @@ public:
PCB_FIELD( const PCB_TEXT& aText, int aFieldId, const wxString& aName = wxEmptyString );
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && PCB_FIELD_T == aItem->Type();
@ -114,6 +117,8 @@ protected:
void swapData( BOARD_ITEM* aImage ) override;
private:
void setId( int aId ) { m_id = aId; }
int m_id; ///< Field index, @see enum MANDATORY_FIELD_T
wxString m_name;

View File

@ -1944,6 +1944,9 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
}
break;
case SHAPE_T::UNDEFINED:
break;
}
}
else

View File

@ -24,6 +24,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <google/protobuf/any.pb.h>
#include <magic_enum.hpp>
#include <bitmaps.h>
#include <core/mirror.h>
#include <macros.h>
@ -35,6 +38,10 @@
#include <geometry/shape_compound.h>
#include <pcb_shape.h>
#include <pcb_painter.h>
#include <api/board/board_types.pb.h>
#include <api/api_enums.h>
#include <api/api_utils.h>
PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType ) :
BOARD_CONNECTED_ITEM( aParent, aItemType ),
@ -54,6 +61,270 @@ PCB_SHAPE::~PCB_SHAPE()
{
}
// TODO: lift out
kiapi::common::types::PolyLine lineChainToProto( const SHAPE_LINE_CHAIN& aSlc )
{
kiapi::common::types::PolyLine msg;
for( int vertex = 0; vertex < aSlc.PointCount(); vertex = aSlc.NextShape( vertex ) )
{
kiapi::common::types::PolyLineNode* node = msg.mutable_nodes()->Add();
if( aSlc.IsPtOnArc( vertex ) )
{
const SHAPE_ARC& arc = aSlc.Arc( aSlc.ArcIndex( vertex ) );
node->mutable_arc()->mutable_start()->set_x_nm( arc.GetP0().x );
node->mutable_arc()->mutable_start()->set_y_nm( arc.GetP0().y );
node->mutable_arc()->mutable_mid()->set_x_nm( arc.GetArcMid().x );
node->mutable_arc()->mutable_mid()->set_y_nm( arc.GetArcMid().y );
node->mutable_arc()->mutable_end()->set_x_nm( arc.GetP1().x );
node->mutable_arc()->mutable_end()->set_y_nm( arc.GetP1().y );
}
else
{
node->mutable_point()->set_x_nm( aSlc.CPoint( vertex ).x );
node->mutable_point()->set_y_nm( aSlc.CPoint( vertex ).y );
}
}
msg.set_closed( aSlc.IsClosed() );
return msg;
}
void PCB_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::GraphicShape msg;
msg.mutable_id()->set_value( m_Uuid.AsStdString() );
msg.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
msg.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
msg.mutable_net()->mutable_code()->set_value( GetNetCode() );
msg.mutable_net()->set_name( GetNetname() );
kiapi::common::types::StrokeAttributes* stroke
= msg.mutable_attributes()->mutable_stroke();
kiapi::common::types::GraphicFillAttributes* fill = msg.mutable_attributes()->mutable_fill();
stroke->mutable_width()->set_value_nm( GetWidth() );
switch( GetLineStyle() )
{
case LINE_STYLE::DEFAULT: stroke->set_style( kiapi::common::types::SLS_DEFAULT ); break;
case LINE_STYLE::SOLID: stroke->set_style( kiapi::common::types::SLS_SOLID ); break;
case LINE_STYLE::DASH: stroke->set_style( kiapi::common::types::SLS_DASH ); break;
case LINE_STYLE::DOT: stroke->set_style( kiapi::common::types::SLS_DOT ); break;
case LINE_STYLE::DASHDOT: stroke->set_style( kiapi::common::types::SLS_DASHDOT ); break;
case LINE_STYLE::DASHDOTDOT: stroke->set_style( kiapi::common::types::SLS_DASHDOTDOT ); break;
default: break;
}
switch( GetFillMode() )
{
case FILL_T::FILLED_SHAPE: fill->set_fill_type( kiapi::common::types::GFT_FILLED ); break;
default: fill->set_fill_type( kiapi::common::types::GFT_UNFILLED ); break;
}
switch( GetShape() )
{
case SHAPE_T::SEGMENT:
{
kiapi::board::types::GraphicSegmentAttributes* segment = msg.mutable_segment();
kiapi::common::PackVector2( *segment->mutable_start(), GetStart() );
kiapi::common::PackVector2( *segment->mutable_end(), GetEnd() );
break;
}
case SHAPE_T::RECTANGLE:
{
kiapi::board::types::GraphicRectangleAttributes* rectangle = msg.mutable_rectangle();
kiapi::common::PackVector2( *rectangle->mutable_top_left(), GetStart() );
kiapi::common::PackVector2( *rectangle->mutable_bottom_right(), GetEnd() );
break;
}
case SHAPE_T::ARC:
{
kiapi::board::types::GraphicArcAttributes* arc = msg.mutable_arc();
kiapi::common::PackVector2( *arc->mutable_start(), GetStart() );
kiapi::common::PackVector2( *arc->mutable_mid(), GetArcMid() );
kiapi::common::PackVector2( *arc->mutable_end(), GetEnd() );
break;
}
case SHAPE_T::CIRCLE:
{
kiapi::board::types::GraphicCircleAttributes* circle = msg.mutable_circle();
kiapi::common::PackVector2( *circle->mutable_center(), GetStart() );
kiapi::common::PackVector2( *circle->mutable_radius_point(), GetEnd() );
break;
}
case SHAPE_T::POLY:
{
kiapi::common::types::PolySet* polyset = msg.mutable_polygon();
for( int idx = 0; idx < GetPolyShape().OutlineCount(); ++idx )
{
const SHAPE_POLY_SET::POLYGON& poly = GetPolyShape().Polygon( idx );
if( poly.empty() )
continue;
kiapi::common::types::PolygonWithHoles* polyMsg = polyset->mutable_polygons()->Add();
polyMsg->mutable_outline()->CopyFrom( lineChainToProto( poly.front() ) );
if( poly.size() > 1 )
{
for( size_t hole = 1; hole < poly.size(); ++hole )
polyMsg->mutable_holes()->Add( lineChainToProto( poly[hole] ) );
}
}
break;
}
case SHAPE_T::BEZIER:
{
kiapi::board::types::GraphicBezierAttributes* bezier = msg.mutable_bezier();
kiapi::common::PackVector2( *bezier->mutable_start(), GetStart() );
kiapi::common::PackVector2( *bezier->mutable_control1(), GetBezierC1() );
kiapi::common::PackVector2( *bezier->mutable_control2(), GetBezierC2() );
kiapi::common::PackVector2( *bezier->mutable_end(), GetEnd() );
break;
}
default:
wxASSERT_MSG( false, "Unhandled shape in PCB_SHAPE::Serialize" );
}
aContainer.PackFrom( msg );
}
// TODO(JE) lift out
SHAPE_LINE_CHAIN lineChainFromProto( const kiapi::common::types::PolyLine& aProto )
{
SHAPE_LINE_CHAIN slc;
for( const kiapi::common::types::PolyLineNode& node : aProto.nodes() )
{
if( node.has_point() )
{
slc.Append( VECTOR2I( node.point().x_nm(), node.point().y_nm() ) );
}
else if( node.has_arc() )
{
slc.Append( SHAPE_ARC( VECTOR2I( node.arc().start().x_nm(), node.arc().start().y_nm() ),
VECTOR2I( node.arc().mid().x_nm(), node.arc().mid().y_nm() ),
VECTOR2I( node.arc().end().x_nm(), node.arc().end().y_nm() ),
0 /* don't care about width here */ ) );
}
}
slc.SetClosed( aProto.closed() );
return slc;
}
bool PCB_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::GraphicShape msg;
if( !aContainer.UnpackTo( &msg ) )
return false;
// Initialize everything to a known state that doesn't get touched by every
// codepath below, to make sure the equality operator is consistent
m_start = {};
m_end = {};
m_arcCenter = {};
m_arcMidData = {};
m_bezierC1 = {};
m_bezierC2 = {};
m_editState = 0;
m_proxyItem = false;
m_endsSwapped = false;
const_cast<KIID&>( m_Uuid ) = KIID( msg.id().value() );
SetLocked( msg.locked() == kiapi::common::types::LS_LOCKED );
SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( msg.layer() ) );
SetNetCode( msg.net().code().value() );
SetFilled( msg.attributes().fill().fill_type() == kiapi::common::types::GFT_FILLED );
SetWidth( msg.attributes().stroke().width().value_nm() );
switch( msg.attributes().stroke().style() )
{
case kiapi::common::types::SLS_DEFAULT: SetLineStyle( LINE_STYLE::DEFAULT ); break;
case kiapi::common::types::SLS_SOLID: SetLineStyle( LINE_STYLE::SOLID ); break;
case kiapi::common::types::SLS_DASH: SetLineStyle( LINE_STYLE::DASH ); break;
case kiapi::common::types::SLS_DOT: SetLineStyle( LINE_STYLE::DOT ); break;
case kiapi::common::types::SLS_DASHDOT: SetLineStyle( LINE_STYLE::DASHDOT ); break;
case kiapi::common::types::SLS_DASHDOTDOT: SetLineStyle( LINE_STYLE::DASHDOTDOT ); break;
default: break;
}
if( msg.has_segment() )
{
SetShape( SHAPE_T::SEGMENT );
SetStart( kiapi::common::UnpackVector2( msg.segment().start() ) );
SetEnd( kiapi::common::UnpackVector2( msg.segment().end() ) );
}
else if( msg.has_rectangle() )
{
SetShape( SHAPE_T::RECTANGLE );
SetStart( kiapi::common::UnpackVector2( msg.rectangle().top_left() ) );
SetEnd( kiapi::common::UnpackVector2( msg.rectangle().bottom_right() ) );
}
else if( msg.has_arc() )
{
SetShape( SHAPE_T::ARC );
SetArcGeometry( kiapi::common::UnpackVector2( msg.arc().start() ),
kiapi::common::UnpackVector2( msg.arc().mid() ),
kiapi::common::UnpackVector2( msg.arc().end() ) );
}
else if( msg.has_circle() )
{
SetShape( SHAPE_T::CIRCLE );
SetStart( kiapi::common::UnpackVector2( msg.circle().center() ) );
SetEnd( kiapi::common::UnpackVector2( msg.circle().radius_point() ) );
}
else if( msg.has_polygon() )
{
SetShape( SHAPE_T::POLY );
const auto& polyMsg = msg.polygon().polygons();
SHAPE_POLY_SET sps;
for( const kiapi::common::types::PolygonWithHoles& polygonWithHoles : polyMsg )
{
SHAPE_POLY_SET::POLYGON polygon;
polygon.emplace_back( lineChainFromProto( polygonWithHoles.outline() ) );
for( const kiapi::common::types::PolyLine& holeMsg : polygonWithHoles.holes() )
polygon.emplace_back( lineChainFromProto( holeMsg ) );
sps.AddPolygon( polygon );
}
SetPolyShape( sps );
}
else if( msg.has_bezier() )
{
SetShape( SHAPE_T::BEZIER );
SetStart( kiapi::common::UnpackVector2( msg.bezier().start() ) );
SetBezierC1( kiapi::common::UnpackVector2( msg.bezier().control1() ) );
SetBezierC2( kiapi::common::UnpackVector2( msg.bezier().control2() ) );
SetEnd( kiapi::common::UnpackVector2( msg.bezier().end() ) );
}
return true;
}
bool PCB_SHAPE::IsType( const std::vector<KICAD_T>& aScanTypes ) const
{

View File

@ -57,6 +57,9 @@ public:
return wxT( "PCB_SHAPE" );
}
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
bool IsConnected() const override;
wxString GetFriendlyName() const override { return EDA_SHAPE::GetFriendlyName(); }

View File

@ -23,6 +23,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <google/protobuf/any.pb.h>
#include <pcb_edit_frame.h>
#include <base_units.h>
#include <bitmaps.h>
@ -37,6 +39,12 @@
#include <geometry/shape_compound.h>
#include <callback_gal.h>
#include <convert_basic_shapes_to_polygon.h>
#include <api/api_enums.h>
#include <api/api_utils.h>
#include <api/board/board_types.pb.h>
using namespace kiapi::common;
PCB_TEXT::PCB_TEXT( BOARD_ITEM* parent, KICAD_T idtype ) :
@ -75,6 +83,106 @@ PCB_TEXT::~PCB_TEXT()
}
void PCB_TEXT::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::Text boardText;
boardText.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
kiapi::common::types::Text& text = *boardText.mutable_text();
text.mutable_id()->set_value( m_Uuid.AsStdString() );
text.mutable_position()->set_x_nm( GetPosition().x );
text.mutable_position()->set_y_nm( GetPosition().y );
text.set_text( GetText().ToStdString() );
text.set_hyperlink( GetHyperlink().ToStdString() );
text.set_locked( IsLocked() ? types::LockedState::LS_LOCKED
: types::LockedState::LS_UNLOCKED );
kiapi::common::types::TextAttributes* attrs = text.mutable_attributes();
if( GetFont() )
attrs->set_font_name( GetFont()->GetName().ToStdString() );
attrs->set_horizontal_alignment(
ToProtoEnum<GR_TEXT_H_ALIGN_T, types::HorizontalAlignment>( GetHorizJustify() ) );
attrs->set_vertical_alignment(
ToProtoEnum<GR_TEXT_V_ALIGN_T, types::VerticalAlignment>( GetVertJustify() ) );
attrs->mutable_angle()->set_value_degrees( GetTextAngleDegrees() );
attrs->set_line_spacing( GetLineSpacing() );
attrs->mutable_stroke_width()->set_value_nm( GetTextThickness() );
attrs->set_italic( IsItalic() );
attrs->set_bold( IsBold() );
attrs->set_underlined( GetAttributes().m_Underlined );
attrs->set_visible( IsVisible() );
attrs->set_mirrored( IsMirrored() );
attrs->set_multiline( IsMultilineAllowed() );
attrs->set_keep_upright( IsKeepUpright() );
attrs->mutable_size()->set_x_nm( GetTextSize().x );
attrs->mutable_size()->set_y_nm( GetTextSize().y );
text.set_knockout( IsKnockout() );
aContainer.PackFrom( boardText );
}
bool PCB_TEXT::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::Text textWrapper;
if( !aContainer.UnpackTo( &textWrapper ) )
return false;
SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( textWrapper.layer() ) );
const kiapi::common::types::Text& text = textWrapper.text();
const_cast<KIID&>( m_Uuid ) = KIID( text.id().value() );
SetPosition( VECTOR2I( text.position().x_nm(), text.position().y_nm() ) );
SetLocked( text.locked() == kiapi::common::types::LockedState::LS_LOCKED );
SetText( wxString( text.text().c_str(), wxConvUTF8 ) );
SetHyperlink( wxString( text.hyperlink().c_str(), wxConvUTF8 ) );
SetIsKnockout( text.knockout() );
if( text.has_attributes() )
{
TEXT_ATTRIBUTES attrs = GetAttributes();
attrs.m_Bold = text.attributes().bold();
attrs.m_Italic = text.attributes().italic();
attrs.m_Underlined = text.attributes().underlined();
attrs.m_Visible = text.attributes().visible();
attrs.m_Mirrored = text.attributes().mirrored();
attrs.m_Multiline = text.attributes().multiline();
attrs.m_KeepUpright = text.attributes().keep_upright();
attrs.m_Size = VECTOR2I( text.attributes().size().x_nm(), text.attributes().size().y_nm() );
if( !text.attributes().font_name().empty() )
{
attrs.m_Font = KIFONT::FONT::GetFont(
wxString( text.attributes().font_name().c_str(), wxConvUTF8 ), attrs.m_Bold,
attrs.m_Italic );
}
attrs.m_Angle = EDA_ANGLE( text.attributes().angle().value_degrees(), DEGREES_T );
attrs.m_LineSpacing = text.attributes().line_spacing();
SetTextThickness( text.attributes().stroke_width().value_nm() );
attrs.m_Halign = FromProtoEnum<GR_TEXT_H_ALIGN_T, types::HorizontalAlignment>(
text.attributes().horizontal_alignment() );
attrs.m_Valign = FromProtoEnum<GR_TEXT_V_ALIGN_T, types::VerticalAlignment>(
text.attributes().vertical_alignment() );
SetAttributes( attrs );
}
return true;
}
wxString PCB_TEXT::GetShownText( bool aAllowExtraText, int aDepth ) const
{
const FOOTPRINT* parentFootprint = GetParentFootprint();

View File

@ -65,6 +65,9 @@ public:
return false;
}
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
void StyleFromSettings( const BOARD_DESIGN_SETTINGS& settings ) override;
/**

View File

@ -23,8 +23,6 @@
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <magic_enum.hpp>
#include <pcb_base_frame.h>
#include <core/mirror.h>
#include <connectivity/connectivity_data.h>
@ -46,6 +44,9 @@
#include <trigo.h>
#include <google/protobuf/any.pb.h>
#include <api/api_enums.h>
#include <api/api_utils.h>
#include <api/api_pcb_utils.h>
#include <api/board/board_types.pb.h>
using KIGFX::PCB_PAINTER;
@ -296,15 +297,15 @@ void PCB_TRACK::Serialize( google::protobuf::Any &aContainer ) const
kiapi::board::types::Track track;
track.mutable_id()->set_value( m_Uuid.AsStdString() );
track.mutable_start()->set_x_nm( GetPosition().x );
track.mutable_start()->set_y_nm( GetPosition().y );
track.mutable_start()->set_x_nm( GetStart().x );
track.mutable_start()->set_y_nm( GetStart().y );
track.mutable_end()->set_x_nm( GetEnd().x );
track.mutable_end()->set_y_nm( GetEnd().y );
track.mutable_width()->set_value_nm( GetWidth() );
track.mutable_layer()->set_layer_id( GetLayer() );
track.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
track.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
track.mutable_net()->set_code( GetNetCode() );
track.mutable_net()->mutable_code()->set_value( GetNetCode() );
track.mutable_net()->set_name( GetNetname() );
aContainer.PackFrom( track );
@ -322,8 +323,8 @@ bool PCB_TRACK::Deserialize( const google::protobuf::Any &aContainer )
SetStart( VECTOR2I( track.start().x_nm(), track.start().y_nm() ) );
SetEnd( VECTOR2I( track.end().x_nm(), track.end().y_nm() ) );
SetWidth( track.width().value_nm() );
SetLayer( magic_enum::enum_cast<PCB_LAYER_ID>( track.layer().layer_id() ).value_or( F_Cu ) );
SetNetCode( track.net().code() );
SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( track.layer() ) );
SetNetCode( track.net().code().value() );
SetLocked( track.locked() == kiapi::common::types::LockedState::LS_LOCKED );
return true;
@ -335,17 +336,17 @@ void PCB_ARC::Serialize( google::protobuf::Any &aContainer ) const
kiapi::board::types::Arc arc;
arc.mutable_id()->set_value( m_Uuid.AsStdString() );
arc.mutable_start()->set_x_nm( GetPosition().x );
arc.mutable_start()->set_y_nm( GetPosition().y );
arc.mutable_start()->set_x_nm( GetStart().x );
arc.mutable_start()->set_y_nm( GetStart().y );
arc.mutable_mid()->set_x_nm( GetMid().x );
arc.mutable_mid()->set_y_nm( GetMid().y );
arc.mutable_end()->set_x_nm( GetEnd().x );
arc.mutable_end()->set_y_nm( GetEnd().y );
arc.mutable_width()->set_value_nm( GetWidth() );
arc.mutable_layer()->set_layer_id( GetLayer() );
arc.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
arc.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
arc.mutable_net()->set_code( GetNetCode() );
arc.mutable_net()->mutable_code()->set_value( GetNetCode() );
arc.mutable_net()->set_name( GetNetname() );
aContainer.PackFrom( arc );
@ -364,8 +365,8 @@ bool PCB_ARC::Deserialize( const google::protobuf::Any &aContainer )
SetMid( VECTOR2I( arc.mid().x_nm(), arc.mid().y_nm() ) );
SetEnd( VECTOR2I( arc.end().x_nm(), arc.end().y_nm() ) );
SetWidth( arc.width().value_nm() );
SetLayer( magic_enum::enum_cast<PCB_LAYER_ID>( arc.layer().layer_id() ).value_or( F_Cu ) );
SetNetCode( arc.net().code() );
SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( arc.layer() ) );
SetNetCode( arc.net().code().value() );
SetLocked( arc.locked() == kiapi::common::types::LockedState::LS_LOCKED );
return true;
@ -379,15 +380,22 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
via.mutable_id()->set_value( m_Uuid.AsStdString() );
via.mutable_position()->set_x_nm( GetPosition().x );
via.mutable_position()->set_y_nm( GetPosition().y );
via.mutable_pad_diameter()->set_value_nm( GetWidth() );
via.mutable_drill_diameter()->set_value_nm( GetDrillValue() );
kiapi::board::types::PadStack* padstack = via.mutable_pad_stack();
padstack->set_type( GetViaType() == VIATYPE::BLIND_BURIED
? kiapi::board::types::PadStackType::PST_BLIND_BURIED
: kiapi::board::types::PadStackType::PST_THROUGH );
padstack->mutable_start_layer()->set_layer_id( m_layer );
padstack->mutable_end_layer()->set_layer_id( m_bottomLayer );
padstack->set_start_layer(
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( m_layer ) );
padstack->set_end_layer(
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( m_bottomLayer ) );
kiapi::common::PackVector2( *padstack->mutable_drill_diameter(),
{ GetDrillValue(), GetDrillValue() } );
kiapi::board::types::PadStackLayer* stackLayer = padstack->add_layers();
kiapi::board::PackLayerSet( *stackLayer->mutable_layers(), GetLayerSet() );
kiapi::common::PackVector2( *stackLayer->mutable_size(),
{ GetWidth(), GetWidth() } );
kiapi::board::types::UnconnectedLayerRemoval ulr;
@ -409,7 +417,7 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
via.mutable_net()->set_code( GetNetCode() );
via.mutable_net()->mutable_code()->set_value( GetNetCode() );
via.mutable_net()->set_name( GetNetname() );
aContainer.PackFrom( via );
@ -426,11 +434,17 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
const_cast<KIID&>( m_Uuid ) = KIID( via.id().value() );
SetStart( VECTOR2I( via.position().x_nm(), via.position().y_nm() ) );
SetEnd( GetStart() );
SetWidth( via.pad_diameter().value_nm() );
SetDrill( via.drill_diameter().value_nm() );
SetDrill( via.pad_stack().drill_diameter().x_nm() );
const kiapi::board::types::PadStack& padstack = via.pad_stack();
// We don't yet support complex padstacks for vias
if( padstack.layers_size() == 1 )
{
const kiapi::board::types::PadStackLayer& layer = padstack.layers( 0 );
SetWidth( layer.size().x_nm() );
}
switch( padstack.type() )
{
case kiapi::board::types::PadStackType::PST_BLIND_BURIED:
@ -444,10 +458,11 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
if( GetViaType() != VIATYPE::THROUGH )
{
m_layer = magic_enum::enum_cast<PCB_LAYER_ID>( padstack.start_layer().layer_id() )
.value_or( F_Cu );
m_bottomLayer = magic_enum::enum_cast<PCB_LAYER_ID>( padstack.end_layer().layer_id() )
.value_or( B_Cu );
m_layer = FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
padstack.start_layer() );
m_bottomLayer = FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
padstack.end_layer() );
}
else
{
@ -474,7 +489,7 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
break;
}
SetNetCode( via.net().code() );
SetNetCode( via.net().code().value() );
SetLocked( via.locked() == kiapi::common::types::LockedState::LS_LOCKED );
return true;

View File

@ -24,7 +24,6 @@
#include "pcb_scripting_tool.h"
#include <action_plugin.h>
#include <api/api_plugin_manager.h>
#include <kiface_ids.h>
#include <kiway.h>
#include <macros.h>
@ -38,6 +37,10 @@
#include <wx/string.h>
#include <launch_ext.h>
#ifdef KICAD_IPC_API
#include <api/api_plugin_manager.h>
#endif
using initfunc = PyObject* (*)(void);
SCRIPTING_TOOL::SCRIPTING_TOOL() :
@ -108,8 +111,10 @@ int SCRIPTING_TOOL::reloadPlugins( const TOOL_EVENT& aEvent )
return -1;
}
#ifdef KICAD_IPC_API
// TODO move this elsewhere when SWIG plugins are removed
Pgm().GetPluginManager().ReloadPlugins();
#endif
if( !m_isFootprintEditor )
{
@ -161,6 +166,6 @@ int SCRIPTING_TOOL::showPluginFolder( const TOOL_EVENT& aEvent )
void SCRIPTING_TOOL::setTransitions()
{
Go( &SCRIPTING_TOOL::reloadPlugins, PCB_ACTIONS::pluginsReload.MakeEvent() );
Go( &SCRIPTING_TOOL::reloadPlugins, ACTIONS::pluginsReload.MakeEvent() );
Go( &SCRIPTING_TOOL::showPluginFolder, PCB_ACTIONS::pluginsShowFolder.MakeEvent() );
}

View File

@ -284,8 +284,12 @@ void PCB_EDIT_FRAME::ReCreateHToolbar()
// Add SWIG and API plugins
bool scriptingAvailable = SCRIPTING::IsWxAvailable();
#ifdef KICAD_IPC_API
bool haveApiPlugins = Pgm().GetCommonSettings()->m_Api.enable_server &&
!Pgm().GetPluginManager().GetActionsForScope( PLUGIN_ACTION_SCOPE::PCB ).empty();
#else
bool haveApiPlugins = false;
#endif
if( scriptingAvailable || haveApiPlugins )
{
@ -298,7 +302,7 @@ void PCB_EDIT_FRAME::ReCreateHToolbar()
}
if( haveApiPlugins )
AddApiPluginTools();
addApiPluginTools();
}
// after adding the buttons to the toolbar, must call Realize() to reflect the changes
@ -306,35 +310,6 @@ void PCB_EDIT_FRAME::ReCreateHToolbar()
}
void PCB_EDIT_FRAME::AddApiPluginTools()
{
// TODO: Add user control over visibility and order
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
mgr.ButtonBindings().clear();
std::vector<const PLUGIN_ACTION*> actions = mgr.GetActionsForScope( PLUGIN_ACTION_SCOPE::PCB );
for( auto& action : actions )
{
if( !action->show_button )
continue;
const wxBitmapBundle& icon = KIPLATFORM::UI::IsDarkTheme() && action->icon_dark.IsOk()
? action->icon_dark
: action->icon_light;
wxAuiToolBarItem* button = m_mainToolBar->AddTool( wxID_ANY, wxEmptyString, icon,
action->name );
Connect( button->GetId(), wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler( PCB_EDIT_FRAME::OnApiPluginButton ) );
mgr.ButtonBindings().insert( { button->GetId(), action->identifier } );
}
}
void PCB_EDIT_FRAME::ReCreateOptToolbar()
{
// Note:

View File

@ -985,12 +985,6 @@ TOOL_ACTION PCB_ACTIONS::defaultPadProperties( TOOL_ACTION_ARGS()
// SCRIPTING TOOL
//
TOOL_ACTION PCB_ACTIONS::pluginsReload( TOOL_ACTION_ARGS()
.Name( "pcbnew.ScriptingTool.pluginsReload" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Refresh Plugins" ) )
.Tooltip( _( "Reload all python plugins and refresh plugin menus" ) )
.Icon( BITMAPS::reload ) );
TOOL_ACTION PCB_ACTIONS::pluginsShowFolder( TOOL_ACTION_ARGS()
.Name( "pcbnew.ScriptingTool.pluginsShowFolder" )

View File

@ -399,7 +399,6 @@ public:
static TOOL_ACTION zoneDuplicate;
/// Scripting Actions
static TOOL_ACTION pluginsReload;
static TOOL_ACTION pluginsShowFolder;
// Global edit tool

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,272 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 1.143,
"height": 2.032,
"width": 2.032
},
"silk_line_width": 0.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_near_hole": "error",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.0,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "api_kitchen_sink.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"legacy_lib_dir": "",
"legacy_lib_list": []
},
"sheets": [],
"text_variables": {}
}

View File

@ -20,6 +20,7 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
## Unit tests
add_subdirectory( api )
add_subdirectory( common )
add_subdirectory( gerbview )
add_subdirectory( eeschema )

View File

@ -15,29 +15,45 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
include_directories( BEFORE ${INC_BEFORE} )
include_directories(
${INC_AFTER}
set( QA_API_SRCS
test_api_module.cpp
test_api_enums.cpp
test_api_proto.cpp
)
add_executable( enum_exporter WIN32
enum_exporter.cpp
add_executable( qa_api
${QA_API_SRCS}
)
target_link_libraries( enum_exporter
add_dependencies( qa_api pcbnew )
target_link_libraries( qa_api
kiapi
qa_utils
qa_pcbnew_utils
pcbnew_kiface_objects
3d-viewer
connectivity
pcbcommon
pnsrouter
gal
common
idf3
markdown_lib
${PCBNEW_IO_LIBRARIES}
${wxWidgets_LIBRARIES}
Boost::headers
Boost::unit_test_framework
${PCBNEW_EXTRA_LIBS} # -lrt must follow Boost
)
target_include_directories( enum_exporter PRIVATE
target_include_directories( qa_api PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/common
${CMAKE_SOURCE_DIR}/qa/mocks/include
${CMAKE_SOURCE_DIR}/pcbnew
${CMAKE_CURRENT_SOURCE_DIR}
$<TARGET_PROPERTY:magic_enum,INTERFACE_INCLUDE_DIRECTORIES>
)
if( MSVC )
# The cli needs subsystem:console or else we can't link wmain/main
set_target_properties(enum_exporter PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
set_target_properties(enum_exporter PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
endif()
kicad_add_boost_test( qa_api qa_api )

Some files were not shown because too many files have changed in this diff Show More