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<GetTextExtents, commands::BoundingBoxResponse>(
84 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
85 return tl::unexpected( *busy );
87 RunActionResponse response;
89 if(
frame()->GetToolManager()->RunAction( aRequest.action(),
true ) )
90 response.set_status( RunActionStatus::RAS_OK );
92 response.set_status( RunActionStatus::RAS_INVALID );
101 if( aMsg.type() != DocumentType::DOCTYPE_PCB )
105 e.set_status( ApiStatusCode::AS_UNHANDLED );
106 return tl::unexpected( e );
109 GetOpenDocumentsResponse response;
110 common::types::DocumentSpecifier doc;
112 wxFileName fn(
frame()->GetCurrentFileName() );
114 doc.set_type( DocumentType::DOCTYPE_PCB );
115 doc.set_board_filename( fn.GetFullName() );
117 doc.mutable_project()->set_name(
frame()->
Prj().GetProjectName().ToStdString() );
118 doc.mutable_project()->set_path(
frame()->
Prj().GetProjectDirectory().ToStdString() );
120 response.mutable_documents()->Add( std::move( doc ) );
134 return std::make_unique<BOARD_COMMIT>(
frame() );
151 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
154 wxFileName fn(
frame()->GetCurrentFileName() );
155 return 0 == aDocument.board_filename().compare( fn.GetFullName() );
165 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
166 e.set_error_message(
"Tried to create an item in a null container" );
167 return tl::unexpected( e );
173 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
174 e.set_error_message( fmt::format(
"Tried to create a pad in {}, which is not a footprint",
176 return tl::unexpected( e );
181 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
182 e.set_error_message( fmt::format(
"Tried to create a footprint in {}, which is not a board",
184 return tl::unexpected( e );
192 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
193 e.set_error_message( fmt::format(
"Tried to create an item of type {}, which is unhandled",
194 magic_enum::enum_name( aType ) ) );
195 return tl::unexpected( e );
204 const types::ItemHeader &aHeader,
205 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
206 std::function<
void( ItemStatus, google::protobuf::Any )> aItemHandler )
212 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
215 e.set_status( ApiStatusCode::AS_UNHANDLED );
216 return tl::unexpected( e );
218 else if( !containerResult )
220 e.CopyFrom( containerResult.error() );
221 return tl::unexpected( e );
227 if( containerResult->has_value() )
229 const KIID& containerId = **containerResult;
230 std::optional<BOARD_ITEM*> optItem =
getItemById( containerId );
238 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
239 e.set_error_message( fmt::format(
240 "The requested container {} is not a valid board item container",
242 return tl::unexpected( e );
247 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
248 e.set_error_message( fmt::format(
249 "The requested container {} does not exist in this document",
251 return tl::unexpected( e );
257 for(
const google::protobuf::Any& anyItem : aItems )
264 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
265 status.set_error_message( fmt::format(
"Could not decode a valid type from {}",
266 anyItem.type_url() ) );
267 aItemHandler( status, anyItem );
274 if( !creationResult )
276 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
277 status.set_error_message( creationResult.error().error_message() );
278 aItemHandler( status, anyItem );
282 std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
284 if( !item->Deserialize( anyItem ) )
286 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
287 e.set_error_message( fmt::format(
"could not unpack {} from request",
288 item->GetClass().ToStdString() ) );
289 return tl::unexpected( e );
292 std::optional<BOARD_ITEM*> optItem =
getItemById( item->m_Uuid );
294 if( aCreate && optItem )
296 status.set_code( ItemStatusCode::ISC_EXISTING );
297 status.set_error_message( fmt::format(
"an item with UUID {} already exists",
298 item->m_Uuid.AsStdString() ) );
299 aItemHandler( status, anyItem );
302 else if( !aCreate && !optItem )
304 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
305 status.set_error_message( fmt::format(
"an item with UUID {} does not exist",
306 item->m_Uuid.AsStdString() ) );
307 aItemHandler( status, anyItem );
313 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
314 status.set_error_message( fmt::format(
"attempted to add item on disabled layer {}",
315 LayerName( item->GetLayer() ).ToStdString() ) );
316 aItemHandler( status, anyItem );
320 status.set_code( ItemStatusCode::ISC_OK );
321 google::protobuf::Any newItem;
325 item->Serialize( newItem );
326 commit->
Add( item.release() );
331 commit->
Modify( boardItem );
336 aItemHandler( status, newItem );
342 :
_(
"Added items via API" ) );
346 return ItemRequestStatus::IRS_OK;
353 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
354 return tl::unexpected( *busy );
360 e.set_status( ApiStatusCode::AS_UNHANDLED );
361 return tl::unexpected( e );
364 GetItemsResponse response;
367 std::vector<BOARD_ITEM*> items;
368 std::set<KICAD_T> typesRequested, typesInserted;
369 bool handledAnything =
false;
371 for(
int typeRaw : aMsg.types() )
373 auto typeMessage =
static_cast<common::types::KiCadObjectType
>( typeRaw );
374 KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
379 typesRequested.emplace( type );
381 if( typesInserted.count( type ) )
389 handledAnything =
true;
390 std::copy( board->
Tracks().begin(), board->
Tracks().end(),
391 std::back_inserter( items ) );
397 handledAnything =
true;
401 std::copy( fp->Pads().begin(), fp->Pads().end(),
402 std::back_inserter( items ) );
411 handledAnything =
true;
414 std::back_inserter( items ) );
425 if( !handledAnything )
428 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
429 e.set_error_message(
"none of the requested types are valid for a Board object" );
430 return tl::unexpected( e );
435 if( !typesRequested.count( item->Type() ) )
438 google::protobuf::Any itemBuf;
439 item->Serialize( itemBuf );
440 response.mutable_items()->Add( std::move( itemBuf ) );
443 response.set_status( ItemRequestStatus::IRS_OK );
452 std::vector<BOARD_ITEM*> validatedItems;
454 for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
458 validatedItems.push_back( item );
459 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
489 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
490 return tl::unexpected( *busy );
494 if( !documentValidation )
495 return tl::unexpected( documentValidation.error() );
497 BoardStackupResponse response;
498 google::protobuf::Any any;
502 any.UnpackTo( response.mutable_stackup() );
509 GetGraphicsDefaults& aMsg,
512 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
513 return tl::unexpected( *busy );
517 if( !documentValidation )
518 return tl::unexpected( documentValidation.error() );
521 GraphicsDefaultsResponse response;
524 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
525 kiapi::board::BLC_SILKSCREEN,
526 kiapi::board::BLC_COPPER,
527 kiapi::board::BLC_EDGES,
528 kiapi::board::BLC_COURTYARD,
529 kiapi::board::BLC_FABRICATION,
530 kiapi::board::BLC_OTHER
535 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
537 l->set_layer( classOrder[i] );
540 kiapi::common::types::TextAttributes*
text = l->mutable_text();
553 GetTextExtents& aMsg,
558 google::protobuf::Any any;
559 any.PackFrom( aMsg.text() );
561 if( !
text.Deserialize( any ) )
564 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
565 e.set_error_message(
"Could not decode text in GetTextExtents message" );
566 return tl::unexpected( e );
569 commands::BoundingBoxResponse response;
577 response.mutable_position()->set_x_nm( bbox.
GetPosition().
x );
578 response.mutable_position()->set_y_nm( bbox.
GetPosition().
y );
579 response.mutable_size()->set_x_nm( bbox.
GetSize().
x );
580 response.mutable_size()->set_y_nm( bbox.
GetSize().
y );
589 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
590 return tl::unexpected( *busy );
594 if( !documentValidation )
595 return tl::unexpected( documentValidation.error() );
598 std::vector<EDA_ITEM*> toSelect;
600 for(
const kiapi::common::types::KIID&
id : aMsg.items() )
602 if( std::optional<BOARD_ITEM*> item =
getItemById(
KIID(
id.value() ) ) )
603 toSelect.emplace_back(
static_cast<EDA_ITEM*
>( *item ) );
606 if( toSelect.empty() )
609 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
610 e.set_error_message( fmt::format(
"None of the given items exist on the board",
611 aMsg.board().board_filename() ) );
612 return tl::unexpected( e );
631 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
632 return tl::unexpected( *busy );
636 if( !documentValidation )
637 return tl::unexpected( documentValidation.error() );
639 NetsResponse response;
642 std::set<wxString> netclassFilter;
644 for(
const std::string& nc : aMsg.netclass_filter() )
645 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
651 if( !netclassFilter.empty() && nc && !netclassFilter.count( nc->
GetName() ) )
654 board::types::Net* netProto = response.add_nets();
655 netProto->set_name( net->GetNetname() );
656 netProto->mutable_code()->set_value( net->GetNetCode() );
666 if( std::optional<ApiResponseStatus> busy =
checkForBusy() )
667 return tl::unexpected( *busy );
671 if( !documentValidation )
672 return tl::unexpected( documentValidation.error() );
674 if( aMsg.zones().empty() )
677 frame()->CallAfter( [mgr]()
686 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
687 return tl::unexpected( e );
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
std::unique_ptr< EDA_ITEM > CreateItemForType(KICAD_T aType, EDA_ITEM *aContainer)
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
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
HANDLER_RESULT< commands::BoundingBoxResponse > handleGetTextExtents(GetTextExtents &aMsg, const HANDLER_CONTEXT &aCtx)
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< 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)
void pushCurrentCommit(const HANDLER_CONTEXT &aCtx, const wxString &aMessage) override
HANDLER_RESULT< commands::GetOpenDocumentsResponse > handleGetOpenDocuments(commands::GetOpenDocuments &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
const FOOTPRINTS & Footprints() const
const TRACKS & Tracks() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
const Vec & GetPosition() const
const SizeVec & GetSize() 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.
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 wxString GetFriendlyName() const
std::string AsStdString() const
bool Contains(PCB_LAYER_ID aLayer)
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.
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.
std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Class to handle a set of BOARD_ITEMs.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
@ 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)