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
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)