21#include <magic_enum.hpp>
45#include <api/common/types/base_types.pb.h>
47using namespace kiapi::common::commands;
48using kiapi::common::types::CommandStatus;
49using kiapi::common::types::DocumentType;
50using kiapi::common::types::ItemRequestStatus;
57 registerHandler<GetOpenDocuments, GetOpenDocumentsResponse>(
64 registerHandler<GetGraphicsDefaults, GraphicsDefaultsResponse>(
66 registerHandler<GetBoundingBox, GetBoundingBoxResponse>(
68 registerHandler<GetTextExtents, types::Box2>(
70 registerHandler<GetPadShapeAsPolygon, PadShapeAsPolygonResponse>(
72 registerHandler<GetTitleBlockInfo, types::TitleBlockInfo>(
90 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
91 return tl::unexpected( *busy );
93 RunActionResponse response;
95 if(
frame()->GetToolManager()->RunAction( aRequest.action(),
true ) )
96 response.set_status( RunActionStatus::RAS_OK );
98 response.set_status( RunActionStatus::RAS_INVALID );
107 if( aMsg.type() != DocumentType::DOCTYPE_PCB )
111 e.set_status( ApiStatusCode::AS_UNHANDLED );
112 return tl::unexpected( e );
115 GetOpenDocumentsResponse response;
116 common::types::DocumentSpecifier doc;
118 wxFileName fn(
frame()->GetCurrentFileName() );
120 doc.set_type( DocumentType::DOCTYPE_PCB );
121 doc.set_board_filename( fn.GetFullName() );
123 doc.mutable_project()->set_name(
frame()->
Prj().GetProjectName().ToStdString() );
124 doc.mutable_project()->set_path(
frame()->
Prj().GetProjectDirectory().ToStdString() );
126 response.mutable_documents()->Add( std::move( doc ) );
140 return std::make_unique<BOARD_COMMIT>(
frame() );
157 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
160 wxFileName fn(
frame()->GetCurrentFileName() );
161 return 0 == aDocument.board_filename().compare( fn.GetFullName() );
171 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
172 e.set_error_message(
"Tried to create an item in a null container" );
173 return tl::unexpected( e );
179 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
180 e.set_error_message( fmt::format(
"Tried to create a pad in {}, which is not a footprint",
182 return tl::unexpected( e );
187 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
188 e.set_error_message( fmt::format(
"Tried to create a footprint in {}, which is not a board",
190 return tl::unexpected( e );
198 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
199 e.set_error_message( fmt::format(
"Tried to create an item of type {}, which is unhandled",
200 magic_enum::enum_name( aType ) ) );
201 return tl::unexpected( e );
210 const types::ItemHeader &aHeader,
211 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
212 std::function<
void( ItemStatus, google::protobuf::Any )> aItemHandler )
218 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
221 e.set_status( ApiStatusCode::AS_UNHANDLED );
222 return tl::unexpected( e );
224 else if( !containerResult )
226 e.CopyFrom( containerResult.error() );
227 return tl::unexpected( e );
233 if( containerResult->has_value() )
235 const KIID& containerId = **containerResult;
236 std::optional<BOARD_ITEM*> optItem =
getItemById( containerId );
244 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
245 e.set_error_message( fmt::format(
246 "The requested container {} is not a valid board item container",
248 return tl::unexpected( e );
253 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
254 e.set_error_message( fmt::format(
255 "The requested container {} does not exist in this document",
257 return tl::unexpected( e );
263 for(
const google::protobuf::Any& anyItem : aItems )
270 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
271 status.set_error_message( fmt::format(
"Could not decode a valid type from {}",
272 anyItem.type_url() ) );
273 aItemHandler( status, anyItem );
280 if( !creationResult )
282 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
283 status.set_error_message( creationResult.error().error_message() );
284 aItemHandler( status, anyItem );
288 std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
290 if( !item->Deserialize( anyItem ) )
292 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
293 e.set_error_message( fmt::format(
"could not unpack {} from request",
294 item->GetClass().ToStdString() ) );
295 return tl::unexpected( e );
298 std::optional<BOARD_ITEM*> optItem =
getItemById( item->m_Uuid );
300 if( aCreate && optItem )
302 status.set_code( ItemStatusCode::ISC_EXISTING );
303 status.set_error_message( fmt::format(
"an item with UUID {} already exists",
304 item->m_Uuid.AsStdString() ) );
305 aItemHandler( status, anyItem );
308 else if( !aCreate && !optItem )
310 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
311 status.set_error_message( fmt::format(
"an item with UUID {} does not exist",
312 item->m_Uuid.AsStdString() ) );
313 aItemHandler( status, anyItem );
319 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
320 status.set_error_message( fmt::format(
"attempted to add item on disabled layer {}",
321 LayerName( item->GetLayer() ).ToStdString() ) );
322 aItemHandler( status, anyItem );
326 status.set_code( ItemStatusCode::ISC_OK );
327 google::protobuf::Any newItem;
331 item->Serialize( newItem );
332 commit->
Add( item.release() );
337 commit->
Modify( boardItem );
342 aItemHandler( status, newItem );
348 :
_(
"Added items via API" ) );
352 return ItemRequestStatus::IRS_OK;
359 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
360 return tl::unexpected( *busy );
366 e.set_status( ApiStatusCode::AS_UNHANDLED );
367 return tl::unexpected( e );
370 GetItemsResponse response;
373 std::vector<BOARD_ITEM*> items;
374 std::set<KICAD_T> typesRequested, typesInserted;
375 bool handledAnything =
false;
377 for(
int typeRaw : aMsg.types() )
379 auto typeMessage =
static_cast<common::types::KiCadObjectType
>( typeRaw );
380 KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
385 typesRequested.emplace( type );
387 if( typesInserted.count( type ) )
395 handledAnything =
true;
396 std::copy( board->
Tracks().begin(), board->
Tracks().end(),
397 std::back_inserter( items ) );
403 handledAnything =
true;
407 std::copy( fp->Pads().begin(), fp->Pads().end(),
408 std::back_inserter( items ) );
417 handledAnything =
true;
420 std::back_inserter( items ) );
428 handledAnything =
true;
429 bool inserted =
false;
435 items.emplace_back( item );
451 if( !handledAnything )
454 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
455 e.set_error_message(
"none of the requested types are valid for a Board object" );
456 return tl::unexpected( e );
461 if( !typesRequested.count( item->Type() ) )
464 google::protobuf::Any itemBuf;
465 item->Serialize( itemBuf );
466 response.mutable_items()->Add( std::move( itemBuf ) );
469 response.set_status( ItemRequestStatus::IRS_OK );
478 std::vector<BOARD_ITEM*> validatedItems;
480 for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
484 validatedItems.push_back( item );
485 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
515 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
516 return tl::unexpected( *busy );
520 if( !documentValidation )
521 return tl::unexpected( documentValidation.error() );
523 BoardStackupResponse response;
524 google::protobuf::Any any;
528 any.UnpackTo( response.mutable_stackup() );
535 GetGraphicsDefaults& aMsg,
538 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
539 return tl::unexpected( *busy );
543 if( !documentValidation )
544 return tl::unexpected( documentValidation.error() );
547 GraphicsDefaultsResponse response;
550 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
551 kiapi::board::BLC_SILKSCREEN,
552 kiapi::board::BLC_COPPER,
553 kiapi::board::BLC_EDGES,
554 kiapi::board::BLC_COURTYARD,
555 kiapi::board::BLC_FABRICATION,
556 kiapi::board::BLC_OTHER
561 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
563 l->set_layer( classOrder[i] );
566 kiapi::common::types::TextAttributes*
text = l->mutable_text();
581 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
582 return tl::unexpected( *busy );
588 e.set_status( ApiStatusCode::AS_UNHANDLED );
589 return tl::unexpected( e );
592 GetBoundingBoxResponse response;
593 bool includeText = aMsg.mode() == BoundingBoxMode::BBM_ITEM_AND_CHILD_TEXT;
595 for(
const types::KIID& idMsg : aMsg.items() )
597 KIID id( idMsg.value() );
598 std::optional<BOARD_ITEM*> optItem =
getItemById(
id );
607 bbox =
static_cast<FOOTPRINT*
>( item )->GetBoundingBox( includeText );
611 response.add_items()->set_value( idMsg.value() );
612 PackBox2( *response.add_boxes(), bbox );
620 GetTextExtents& aMsg,
625 google::protobuf::Any any;
626 any.PackFrom( aMsg.text() );
628 if( !
text.Deserialize( any ) )
631 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
632 e.set_error_message(
"Could not decode text in GetTextExtents message" );
633 return tl::unexpected( e );
636 types::Box2 response;
644 response.mutable_position()->set_x_nm( bbox.
GetPosition().
x );
645 response.mutable_position()->set_y_nm( bbox.
GetPosition().
y );
646 response.mutable_size()->set_x_nm( bbox.
GetSize().
x );
647 response.mutable_size()->set_y_nm( bbox.
GetSize().
y );
654 GetPadShapeAsPolygon& aMsg,
659 if( !documentValidation )
660 return tl::unexpected( documentValidation.error() );
663 PadShapeAsPolygonResponse response;
664 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( aMsg.layer() );
666 for(
const types::KIID& padRequest : aMsg.pads() )
668 KIID id( padRequest.value() );
669 std::optional<BOARD_ITEM*> optPad =
getItemById(
id );
671 if( !optPad || ( *optPad )->Type() !=
PCB_PAD_T )
674 response.add_pads()->set_value( padRequest.value() );
677 pad->TransformShapeToPolygon( poly,
pad->Padstack().EffectiveLayerFor( layer ), 0,
680 types::PolygonWithHoles* polyMsg = response.mutable_polygons()->Add();
689 GetTitleBlockInfo& aMsg,
694 if( !documentValidation )
695 return tl::unexpected( documentValidation.error() );
700 types::TitleBlockInfo response;
702 response.set_title( block.
GetTitle().ToUTF8() );
703 response.set_date( block.
GetDate().ToUTF8() );
704 response.set_revision( block.
GetRevision().ToUTF8() );
705 response.set_company( block.
GetCompany().ToUTF8() );
706 response.set_comment1( block.
GetComment( 0 ).ToUTF8() );
707 response.set_comment2( block.
GetComment( 1 ).ToUTF8() );
708 response.set_comment3( block.
GetComment( 2 ).ToUTF8() );
709 response.set_comment4( block.
GetComment( 3 ).ToUTF8() );
710 response.set_comment5( block.
GetComment( 4 ).ToUTF8() );
711 response.set_comment6( block.
GetComment( 5 ).ToUTF8() );
712 response.set_comment7( block.
GetComment( 6 ).ToUTF8() );
713 response.set_comment8( block.
GetComment( 7 ).ToUTF8() );
714 response.set_comment9( block.
GetComment( 8 ).ToUTF8() );
723 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
724 return tl::unexpected( *busy );
728 if( !documentValidation )
729 return tl::unexpected( documentValidation.error() );
732 std::vector<EDA_ITEM*> toSelect;
734 for(
const kiapi::common::types::KIID&
id : aMsg.items() )
736 if( std::optional<BOARD_ITEM*> item =
getItemById(
KIID(
id.value() ) ) )
737 toSelect.emplace_back(
static_cast<EDA_ITEM*
>( *item ) );
740 if( toSelect.empty() )
743 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
744 e.set_error_message( fmt::format(
"None of the given items exist on the board",
745 aMsg.board().board_filename() ) );
746 return tl::unexpected( e );
765 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
766 return tl::unexpected( *busy );
770 if( !documentValidation )
771 return tl::unexpected( documentValidation.error() );
773 NetsResponse response;
776 std::set<wxString> netclassFilter;
778 for(
const std::string& nc : aMsg.netclass_filter() )
779 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
785 if( !netclassFilter.empty() && nc && !netclassFilter.count( nc->
GetName() ) )
788 board::types::Net* netProto = response.add_nets();
789 netProto->set_name( net->GetNetname() );
790 netProto->mutable_code()->set_value( net->GetNetCode() );
800 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
801 return tl::unexpected( *busy );
805 if( !documentValidation )
806 return tl::unexpected( documentValidation.error() );
808 if( aMsg.zones().empty() )
811 frame()->CallAfter( [mgr]()
820 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
821 return tl::unexpected( e );
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
std::unique_ptr< EDA_ITEM > CreateItemForType(KICAD_T aType, EDA_ITEM *aContainer)
constexpr int ARC_HIGH_DEF
Base class for API handlers related to editor frames.
virtual void pushCurrentCommit(const HANDLER_CONTEXT &aCtx, const wxString &aMessage)
HANDLER_RESULT< bool > validateDocument(const DocumentSpecifier &aDocument)
HANDLER_RESULT< std::optional< KIID > > validateItemHeaderDocument(const kiapi::common::types::ItemHeader &aHeader)
If the header is valid, returns the item container.
COMMIT * getCurrentCommit(const HANDLER_CONTEXT &aCtx)
std::set< std::string > m_activeClients
virtual std::optional< ApiResponseStatus > checkForBusy()
Checks if the editor can accept commands.
HANDLER_RESULT< commands::GetItemsResponse > handleGetItems(commands::GetItems &aMsg, const HANDLER_CONTEXT &aCtx)
bool validateDocumentInternal(const DocumentSpecifier &aDocument) const override
HANDLER_RESULT< types::TitleBlockInfo > handleGetTitleBlockInfo(commands::GetTitleBlockInfo &aMsg, const HANDLER_CONTEXT &aCtx)
API_HANDLER_PCB(PCB_EDIT_FRAME *aFrame)
static HANDLER_RESULT< std::unique_ptr< BOARD_ITEM > > createItemForType(KICAD_T aType, BOARD_ITEM_CONTAINER *aContainer)
std::optional< BOARD_ITEM * > getItemById(const KIID &aId) const
std::unique_ptr< COMMIT > createCommit() override
Override this to create an appropriate COMMIT subclass for the frame in question.
HANDLER_RESULT< Empty > handleInteractiveMoveItems(InteractiveMoveItems &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< BoardStackupResponse > handleGetStackup(GetBoardStackup &aMsg, const HANDLER_CONTEXT &aCtx)
void deleteItemsInternal(std::map< KIID, ItemDeletionStatus > &aItemsToDelete, const HANDLER_CONTEXT &aCtx) override
std::optional< EDA_ITEM * > getItemFromDocument(const DocumentSpecifier &aDocument, const KIID &aId) override
HANDLER_RESULT< commands::RunActionResponse > handleRunAction(commands::RunAction &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< commands::GetBoundingBoxResponse > handleGetBoundingBox(commands::GetBoundingBox &aMsg, const HANDLER_CONTEXT &aCtx)
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
HANDLER_RESULT< Empty > handleRefillZones(RefillZones &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< GraphicsDefaultsResponse > handleGetGraphicsDefaults(GetGraphicsDefaults &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< types::Box2 > handleGetTextExtents(GetTextExtents &aMsg, const HANDLER_CONTEXT &aCtx)
void pushCurrentCommit(const HANDLER_CONTEXT &aCtx, const wxString &aMessage) override
HANDLER_RESULT< commands::GetOpenDocumentsResponse > handleGetOpenDocuments(commands::GetOpenDocuments &aMsg, const HANDLER_CONTEXT &aCtx)
HANDLER_RESULT< PadShapeAsPolygonResponse > handleGetPadShapeAsPolygon(GetPadShapeAsPolygon &aMsg, const HANDLER_CONTEXT &aCtx)
PCB_EDIT_FRAME * frame() const
HANDLER_RESULT< NetsResponse > handleGetNets(GetNets &aMsg, const HANDLER_CONTEXT &aCtx)
Container for design settings for a BOARD object.
bool m_TextUpright[LAYER_CLASS_COUNT]
int m_TextThickness[LAYER_CLASS_COUNT]
int m_LineThickness[LAYER_CLASS_COUNT]
VECTOR2I m_TextSize[LAYER_CLASS_COUNT]
bool m_TextItalic[LAYER_CLASS_COUNT]
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
void SwapItemData(BOARD_ITEM *aImage)
Swap data between aItem and aImage.
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Information pertinent to a Pcbnew printed circuit board.
BOARD_STACKUP GetStackupOrDefault() const
const NETINFO_LIST & GetNetInfo() const
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
BOARD_ITEM * GetItem(const KIID &aID) const
TITLE_BLOCK & GetTitleBlock()
const FOOTPRINTS & Footprints() const
const TRACKS & Tracks() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
const DRAWINGS & Drawings() const
constexpr const Vec & GetPosition() const
const BOX2< Vec > GetBoundingBoxRotated(const VECTOR2I &aRotCenter, const EDA_ANGLE &aAngle) const
Useful to calculate bounding box of rotated items, when rotation is not cardinal.
constexpr const SizeVec & GetSize() const
Represent a set of changes (additions, deletions or modifications) of a data model (e....
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
static DELETED_BOARD_ITEM * GetInstance()
A base class for most all the KiCad significant classes used in schematics and boards.
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
KICAD_T Type() const
Returns the type of object.
virtual wxString GetFriendlyName() const
std::string AsStdString() const
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
A collection of nets and the parameters used to route or test these nets.
const wxString GetName() const
Gets the consolidated name of this netclass (which may be an aggregate)
Handle the data for a net.
static TOOL_ACTION zoneFillAll
static TOOL_ACTION selectionClear
Clear the current selection.
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
The main frame for Pcbnew.
void SetReferencePoint(const VECTOR2I &aP)
virtual void Serialize(google::protobuf::Any &aContainer) const
Serializes this object to the given Any message.
Represent a set of closed polygons.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Hold the information shown in the lower right corner of a plot, printout, or editing view.
const wxString & GetCompany() const
const wxString & GetRevision() const
const wxString & GetDate() const
const wxString & GetComment(int aIdx) const
const wxString & GetTitle() const
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
PCB_LAYER_ID
A quick note on layer IDs:
std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
void PackPolyLine(kiapi::common::types::PolyLine &aOutput, const SHAPE_LINE_CHAIN &aSlc)
void PackBox2(types::Box2 &aOutput, const BOX2I &aInput)
Class to handle a set of BOARD_ITEMs.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)