KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_handler_pcb.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2023 Jon Evans <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <magic_enum.hpp>
22#include <properties/property.h>
23
24#include <common.h>
25#include <api/api_handler_pcb.h>
26#include <api/api_pcb_utils.h>
27#include <api/api_enums.h>
28#include <api/board_context.h>
29#include <api/api_utils.h>
30#include <base_screen.h>
31#include <board_commit.h>
34#include <footprint.h>
35#include <kicad_clipboard.h>
36#include <netinfo.h>
37#include <pad.h>
38#include <pcb_edit_frame.h>
39#include <pcb_group.h>
40#include <pcb_reference_image.h>
41#include <pcb_shape.h>
42#include <pcb_text.h>
43#include <pcb_textbox.h>
44#include <pcb_track.h>
45#include <pcbnew_id.h>
46#include <pcb_marker.h>
47#include <kiway.h>
48#include <drc/drc_item.h>
63#include <jobs/job_pcb_render.h>
64#include <layer_ids.h>
65#include <project.h>
66#include <tool/tool_manager.h>
67#include <tools/pcb_actions.h>
69#include <zone.h>
70
71#include <api/common/types/base_types.pb.h>
74#include <drc/drc_rule_parser.h>
78#include <wx/ffile.h>
79#include <wx/filename.h>
80
81using namespace kiapi::common::commands;
82using types::CommandStatus;
83using types::DocumentType;
84using types::ItemRequestStatus;
85
86
91
92
93API_HANDLER_PCB::API_HANDLER_PCB( std::shared_ptr<BOARD_CONTEXT> aContext,
94 PCB_EDIT_FRAME* aFrame ) :
95 API_HANDLER_EDITOR( aFrame ),
96 m_context( std::move( aContext ) )
97{
98 wxCHECK( m_context, /* void */ );
99
106
109
115
137
146
163
192
195}
196
197
199{
200 return static_cast<PCB_EDIT_FRAME*>( m_frame );
201}
202
203
204std::optional<ApiResponseStatus> API_HANDLER_PCB::checkForHeadless( const std::string& aCommandName ) const
205{
206 if( frame() )
207 return std::nullopt;
208
209 ApiResponseStatus e;
210 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
211 e.set_error_message( fmt::format( "{} is not available in headless mode", aCommandName ) );
212 return e;
213}
214
215
217 const HANDLER_CONTEXT<RunAction>& aCtx )
218{
219 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RunAction" ) )
220 return tl::unexpected( *headless );
221
222 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
223 return tl::unexpected( *busy );
224
225 RunActionResponse response;
226
227 if( toolManager()->RunAction( aCtx.Request.action(), true ) )
228 response.set_status( RunActionStatus::RAS_OK );
229 else
230 response.set_status( RunActionStatus::RAS_INVALID );
231
232 return response;
233}
234
235
238{
239 if( aCtx.Request.type() != DocumentType::DOCTYPE_PCB )
240 {
241 ApiResponseStatus e;
242 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
243 e.set_status( ApiStatusCode::AS_UNHANDLED );
244 return tl::unexpected( e );
245 }
246
247 GetOpenDocumentsResponse response;
248 common::types::DocumentSpecifier doc;
249
250 wxFileName fn( context()->GetCurrentFileName() );
251
252 doc.set_type( DocumentType::DOCTYPE_PCB );
253 doc.set_board_filename( fn.GetFullName() );
254
255 doc.mutable_project()->set_name( project().GetProjectName().ToStdString() );
256 doc.mutable_project()->set_path( project().GetProjectDirectory().ToStdString() );
257
258 response.mutable_documents()->Add( std::move( doc ) );
259 return response;
260}
261
262
265{
266 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
267 return tl::unexpected( *busy );
268
269 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
270
271 if( !documentValidation )
272 return tl::unexpected( documentValidation.error() );
273
274 context()->SaveBoard();
275 return Empty();
276}
277
278
281{
282 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
283 return tl::unexpected( *busy );
284
285 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
286
287 if( !documentValidation )
288 return tl::unexpected( documentValidation.error() );
289
290 wxFileName boardPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.path() ) ) );
291
292 if( !boardPath.IsOk() || !boardPath.IsDirWritable() )
293 {
294 ApiResponseStatus e;
295 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
296 e.set_error_message( fmt::format( "save path '{}' could not be opened",
297 boardPath.GetFullPath().ToStdString() ) );
298 return tl::unexpected( e );
299 }
300
301 if( boardPath.FileExists()
302 && ( !boardPath.IsFileWritable() || !aCtx.Request.options().overwrite() ) )
303 {
304 ApiResponseStatus e;
305 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
306 e.set_error_message( fmt::format( "save path '{}' exists and cannot be overwritten",
307 boardPath.GetFullPath().ToStdString() ) );
308 return tl::unexpected( e );
309 }
310
311 if( boardPath.GetExt() != FILEEXT::KiCadPcbFileExtension )
312 {
313 ApiResponseStatus e;
314 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
315 e.set_error_message( fmt::format( "save path '{}' must have a kicad_pcb extension",
316 boardPath.GetFullPath().ToStdString() ) );
317 return tl::unexpected( e );
318 }
319
320 BOARD* board = this->board();
321
322 if( board->GetFileName().Matches( boardPath.GetFullPath() ) )
323 {
324 context()->SaveBoard();
325 return Empty();
326 }
327
328 bool includeProject = true;
329
330 if( aCtx.Request.has_options() )
331 includeProject = aCtx.Request.options().include_project();
332
333 context()->SavePcbCopy( boardPath.GetFullPath(), includeProject, /* aHeadless = */ true );
334
335 return Empty();
336}
337
338
341{
342 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RevertDocument" ) )
343 return tl::unexpected( *headless );
344
345 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
346 return tl::unexpected( *busy );
347
348 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
349
350 if( !documentValidation )
351 return tl::unexpected( documentValidation.error() );
352
353 wxFileName fn = project().AbsolutePath( board()->GetFileName() );
354
355 frame()->GetScreen()->SetContentModified( false );
356 frame()->ReleaseFile();
357 frame()->OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ), KICTL_REVERT );
358
359 return Empty();
360}
361
362
363void API_HANDLER_PCB::pushCurrentCommit( const std::string& aClientName, const wxString& aMessage )
364{
365 API_HANDLER_EDITOR::pushCurrentCommit( aClientName, aMessage );
366
367 if( frame() )
368 frame()->Refresh();
369}
370
371
372std::unique_ptr<COMMIT> API_HANDLER_PCB::createCommit()
373{
374 if( frame() )
375 return std::make_unique<BOARD_COMMIT>( frame() );
376
377 return std::make_unique<BOARD_COMMIT>( toolManager(), true, false );
378}
379
380
381std::optional<BOARD_ITEM*> API_HANDLER_PCB::getItemById( const KIID& aId ) const
382{
383 BOARD_ITEM* item = board()->ResolveItem( aId, true );
384
385 if( !item )
386 return std::nullopt;
387
388 return item;
389}
390
391
392tl::expected<bool, ApiResponseStatus> API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
393{
394 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
395 {
396 ApiResponseStatus e;
397 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
398 e.set_error_message( "the requested document is not a board" );
399 return tl::unexpected( e );
400 }
401
402 wxFileName fn( context()->GetCurrentFileName() );
403
404 if( aDocument.board_filename().compare( fn.GetFullName() ) != 0 )
405 {
406 ApiResponseStatus e;
407 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
408 e.set_error_message( fmt::format( "the requested document {} is not open",
409 aDocument.board_filename() ) );
410 return tl::unexpected( e );
411 }
412
413 return true;
414}
415
416
418 BOARD_ITEM_CONTAINER* aContainer )
419{
420 if( !aContainer )
421 {
422 ApiResponseStatus e;
423 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
424 e.set_error_message( "Tried to create an item in a null container" );
425 return tl::unexpected( e );
426 }
427
428 if( aType == PCB_PAD_T && !dynamic_cast<FOOTPRINT*>( aContainer ) )
429 {
430 ApiResponseStatus e;
431 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
432 e.set_error_message( fmt::format( "Tried to create a pad in {}, which is not a footprint",
433 aContainer->GetFriendlyName().ToStdString() ) );
434 return tl::unexpected( e );
435 }
436 else if( aType == PCB_FOOTPRINT_T && !dynamic_cast<BOARD*>( aContainer ) )
437 {
438 ApiResponseStatus e;
439 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
440 e.set_error_message( fmt::format( "Tried to create a footprint in {}, which is not a board",
441 aContainer->GetFriendlyName().ToStdString() ) );
442 return tl::unexpected( e );
443 }
444
445 std::unique_ptr<BOARD_ITEM> created = CreateItemForType( aType, aContainer );
446
447 if( !created )
448 {
449 ApiResponseStatus e;
450 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
451 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
452 magic_enum::enum_name( aType ) ) );
453 return tl::unexpected( e );
454 }
455
456 return created;
457}
458
459
461 const std::string& aClientName,
462 const types::ItemHeader &aHeader,
463 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
464 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
465{
466 ApiResponseStatus e;
467
468 auto containerResult = validateItemHeaderDocument( aHeader );
469
470 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
471 {
472 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
473 e.set_status( ApiStatusCode::AS_UNHANDLED );
474 return tl::unexpected( e );
475 }
476 else if( !containerResult )
477 {
478 e.CopyFrom( containerResult.error() );
479 return tl::unexpected( e );
480 }
481
482 BOARD* board = this->board();
483 BOARD_ITEM_CONTAINER* container = board;
484
485 if( containerResult->has_value() )
486 {
487 const KIID& containerId = **containerResult;
488 std::optional<BOARD_ITEM*> optItem = getItemById( containerId );
489
490 if( optItem )
491 {
492 container = dynamic_cast<BOARD_ITEM_CONTAINER*>( *optItem );
493
494 if( !container )
495 {
496 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
497 e.set_error_message( fmt::format(
498 "The requested container {} is not a valid board item container",
499 containerId.AsStdString() ) );
500 return tl::unexpected( e );
501 }
502 }
503 else
504 {
505 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
506 e.set_error_message( fmt::format(
507 "The requested container {} does not exist in this document",
508 containerId.AsStdString() ) );
509 return tl::unexpected( e );
510 }
511 }
512
513 BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aClientName ) );
514
515 for( const google::protobuf::Any& anyItem : aItems )
516 {
517 ItemStatus status;
518 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
519
520 if( !type )
521 {
522 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
523 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
524 anyItem.type_url() ) );
525 aItemHandler( status, anyItem );
526 continue;
527 }
528
529 if( type == PCB_DIMENSION_T )
530 {
531 board::types::Dimension dimension;
532 anyItem.UnpackTo( &dimension );
533
534 switch( dimension.dimension_style_case() )
535 {
536 case board::types::Dimension::kAligned: type = PCB_DIM_ALIGNED_T; break;
537 case board::types::Dimension::kOrthogonal: type = PCB_DIM_ORTHOGONAL_T; break;
538 case board::types::Dimension::kRadial: type = PCB_DIM_RADIAL_T; break;
539 case board::types::Dimension::kLeader: type = PCB_DIM_LEADER_T; break;
540 case board::types::Dimension::kCenter: type = PCB_DIM_CENTER_T; break;
541 case board::types::Dimension::DIMENSION_STYLE_NOT_SET: break;
542 }
543 }
544
546 createItemForType( *type, container );
547
548 if( !creationResult )
549 {
550 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
551 status.set_error_message( creationResult.error().error_message() );
552 aItemHandler( status, anyItem );
553 continue;
554 }
555
556 std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
557
558 if( !item->Deserialize( anyItem ) )
559 {
560 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
561 e.set_error_message( fmt::format( "could not unpack {} from request",
562 item->GetClass().ToStdString() ) );
563 return tl::unexpected( e );
564 }
565
566 std::optional<BOARD_ITEM*> optItem = getItemById( item->m_Uuid );
567
568 if( aCreate && optItem )
569 {
570 status.set_code( ItemStatusCode::ISC_EXISTING );
571 status.set_error_message( fmt::format( "an item with UUID {} already exists",
572 item->m_Uuid.AsStdString() ) );
573 aItemHandler( status, anyItem );
574 continue;
575 }
576 else if( !aCreate && !optItem )
577 {
578 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
579 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
580 item->m_Uuid.AsStdString() ) );
581 aItemHandler( status, anyItem );
582 continue;
583 }
584
585 if( aCreate && !( board->GetEnabledLayers() & item->GetLayerSet() ).any() )
586 {
587 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
588 status.set_error_message(
589 "attempted to add item with no overlapping layers with the board" );
590 aItemHandler( status, anyItem );
591 continue;
592 }
593
594 status.set_code( ItemStatusCode::ISC_OK );
595 google::protobuf::Any newItem;
596
597 if( aCreate )
598 {
599 if( item->Type() == PCB_FOOTPRINT_T )
600 {
601 // Ensure children have unique identifiers; in case the API client created this new
602 // footprint by cloning an existing one and only changing the parent UUID.
603 item->RunOnChildren(
604 []( BOARD_ITEM* aChild )
605 {
606 aChild->ResetUuid();
607 },
608 RECURSE );
609 }
610
611 item->Serialize( newItem );
612 commit->Add( item.release() );
613 }
614 else
615 {
616 BOARD_ITEM* boardItem = *optItem;
617
618 // Footprints can't be modified by CopyFrom at the moment because the commit system
619 // doesn't currently know what to do with a footprint that has had its children
620 // replaced with other children; which results in things like the view not having its
621 // cached geometry for footprint children updated when you move a footprint around.
622 // And also, groups are special because they can contain any item type, so we
623 // can't use CopyFrom on them either.
624 if( boardItem->Type() == PCB_FOOTPRINT_T || boardItem->Type() == PCB_GROUP_T )
625 {
626 // Save group membership before removal, since Remove() severs the relationship
627 PCB_GROUP* parentGroup = dynamic_cast<PCB_GROUP*>( boardItem->GetParentGroup() );
628
629 commit->Remove( boardItem );
630 item->Serialize( newItem );
631
632 BOARD_ITEM* newBoardItem = item.release();
633 commit->Add( newBoardItem );
634
635 // Restore group membership for the newly added item
636 if( parentGroup )
637 parentGroup->AddItem( newBoardItem );
638 }
639 else
640 {
641 commit->Modify( boardItem );
642 boardItem->CopyFrom( item.get() );
643 boardItem->Serialize( newItem );
644 }
645 }
646
647 aItemHandler( status, newItem );
648 }
649
650 if( !m_activeClients.count( aClientName ) )
651 {
652 pushCurrentCommit( aClientName, aCreate ? _( "Created items via API" )
653 : _( "Modified items via API" ) );
654 }
655
656
657 return ItemRequestStatus::IRS_OK;
658}
659
660
662{
663 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
664 return tl::unexpected( *busy );
665
666 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
667 {
668 ApiResponseStatus e;
669 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
670 e.set_status( ApiStatusCode::AS_UNHANDLED );
671 return tl::unexpected( e );
672 }
673
674 GetItemsResponse response;
675
676 BOARD* board = this->board();
677 std::vector<BOARD_ITEM*> items;
678 std::set<KICAD_T> typesRequested, typesInserted;
679 bool handledAnything = false;
680
681 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
682 {
683 typesRequested.emplace( type );
684
685 if( typesInserted.count( type ) )
686 continue;
687
688 switch( type )
689 {
690 case PCB_TRACE_T:
691 case PCB_ARC_T:
692 case PCB_VIA_T:
693 handledAnything = true;
694 std::copy( board->Tracks().begin(), board->Tracks().end(),
695 std::back_inserter( items ) );
696 typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
697 break;
698
699 case PCB_PAD_T:
700 {
701 handledAnything = true;
702
703 for( FOOTPRINT* fp : board->Footprints() )
704 {
705 std::copy( fp->Pads().begin(), fp->Pads().end(),
706 std::back_inserter( items ) );
707 }
708
709 typesInserted.insert( PCB_PAD_T );
710 break;
711 }
712
713 case PCB_FOOTPRINT_T:
714 {
715 handledAnything = true;
716
717 std::copy( board->Footprints().begin(), board->Footprints().end(),
718 std::back_inserter( items ) );
719
720 typesInserted.insert( PCB_FOOTPRINT_T );
721 break;
722 }
723
724 case PCB_SHAPE_T:
725 case PCB_TEXT_T:
726 case PCB_TEXTBOX_T:
727 case PCB_BARCODE_T:
729 {
730 handledAnything = true;
731 bool inserted = false;
732
733 for( BOARD_ITEM* item : board->Drawings() )
734 {
735 if( item->Type() == type )
736 {
737 items.emplace_back( item );
738 inserted = true;
739 }
740 }
741
742 if( inserted )
743 typesInserted.insert( type );
744
745 break;
746 }
747
748 case PCB_DIMENSION_T:
749 {
750 handledAnything = true;
751 bool inserted = false;
752
753 for( BOARD_ITEM* item : board->Drawings() )
754 {
755 switch (item->Type()) {
757 case PCB_DIM_CENTER_T:
758 case PCB_DIM_RADIAL_T:
760 case PCB_DIM_LEADER_T:
761 items.emplace_back( item );
762 inserted = true;
763 break;
764 default:
765 break;
766 }
767 }
768 // we have to add the dimension subtypes to the requested to get them out
770
771 if( inserted )
773
774 break;
775 }
776
777 case PCB_ZONE_T:
778 {
779 handledAnything = true;
780
781 std::copy( board->Zones().begin(), board->Zones().end(),
782 std::back_inserter( items ) );
783
784 typesInserted.insert( PCB_ZONE_T );
785 break;
786 }
787
788 case PCB_GROUP_T:
789 {
790 handledAnything = true;
791
792 std::copy( board->Groups().begin(), board->Groups().end(),
793 std::back_inserter( items ) );
794
795 typesInserted.insert( PCB_GROUP_T );
796 break;
797 }
798 default:
799 break;
800 }
801 }
802
803 if( !handledAnything )
804 {
805 ApiResponseStatus e;
806 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
807 e.set_error_message( "none of the requested types are valid for a Board object" );
808 return tl::unexpected( e );
809 }
810
811 for( const BOARD_ITEM* item : items )
812 {
813 if( !typesRequested.count( item->Type() ) )
814 continue;
815
816 google::protobuf::Any itemBuf;
817 item->Serialize( itemBuf );
818 response.mutable_items()->Add( std::move( itemBuf ) );
819 }
820
821 response.set_status( ItemRequestStatus::IRS_OK );
822 return response;
823}
824
825
828{
829 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
830 return tl::unexpected( *busy );
831
832 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
833 {
834 ApiResponseStatus e;
835 e.set_status( ApiStatusCode::AS_UNHANDLED );
836 return tl::unexpected( e );
837 }
838
839 GetItemsResponse response;
840
841 std::vector<BOARD_ITEM*> items;
842
843 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
844 {
845 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
846 items.emplace_back( *item );
847 }
848
849 if( items.empty() )
850 {
851 ApiResponseStatus e;
852 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
853 e.set_error_message( "none of the requested IDs were found or valid" );
854 return tl::unexpected( e );
855 }
856
857 for( const BOARD_ITEM* item : items )
858 {
859 google::protobuf::Any itemBuf;
860 item->Serialize( itemBuf );
861 response.mutable_items()->Add( std::move( itemBuf ) );
862 }
863
864 response.set_status( ItemRequestStatus::IRS_OK );
865 return response;
866}
867
868void API_HANDLER_PCB::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
869 const std::string& aClientName )
870{
871 BOARD* board = this->board();
872 std::vector<BOARD_ITEM*> validatedItems;
873
874 for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
875 {
876 if( BOARD_ITEM* item = board->ResolveItem( pair.first, true ) )
877 {
878 validatedItems.push_back( item );
879 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
880 }
881
882 // Note: we don't currently support locking items from API modification, but here is where
883 // to add it in the future (and return IDS_IMMUTABLE)
884 }
885
886 COMMIT* commit = getCurrentCommit( aClientName );
887
888 for( BOARD_ITEM* item : validatedItems )
889 commit->Remove( item );
890
891 if( !m_activeClients.count( aClientName ) )
892 pushCurrentCommit( aClientName, _( "Deleted items via API" ) );
893}
894
895
896std::optional<EDA_ITEM*> API_HANDLER_PCB::getItemFromDocument( const DocumentSpecifier& aDocument,
897 const KIID& aId )
898{
899 if( !validateDocument( aDocument ) )
900 return std::nullopt;
901
902 return getItemById( aId );
903}
904
905
908{
909 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetSelection" ) )
910 return tl::unexpected( *headless );
911
912 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
913 {
914 ApiResponseStatus e;
915 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
916 e.set_status( ApiStatusCode::AS_UNHANDLED );
917 return tl::unexpected( e );
918 }
919
920 std::set<KICAD_T> filter;
921
922 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
923 filter.insert( type );
924
925 TOOL_MANAGER* mgr = toolManager();
926 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
927
928 SelectionResponse response;
929
930 for( EDA_ITEM* item : selectionTool->GetSelection() )
931 {
932 if( filter.empty() || filter.contains( item->Type() ) )
933 item->Serialize( *response.add_items() );
934 }
935
936 return response;
937}
938
939
942{
943 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "ClearSelection" ) )
944 return tl::unexpected( *headless );
945
946 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
947 return tl::unexpected( *busy );
948
949 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
950 {
951 ApiResponseStatus e;
952 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
953 e.set_status( ApiStatusCode::AS_UNHANDLED );
954 return tl::unexpected( e );
955 }
956
957 TOOL_MANAGER* mgr = toolManager();
959 frame()->Refresh();
960
961 return Empty();
962}
963
964
967{
968 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "AddToSelection" ) )
969 return tl::unexpected( *headless );
970
971 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
972 return tl::unexpected( *busy );
973
974 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
975 {
976 ApiResponseStatus e;
977 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
978 e.set_status( ApiStatusCode::AS_UNHANDLED );
979 return tl::unexpected( e );
980 }
981
982 TOOL_MANAGER* mgr = toolManager();
983 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
984
985 std::vector<EDA_ITEM*> toAdd;
986
987 for( const types::KIID& id : aCtx.Request.items() )
988 {
989 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
990 toAdd.emplace_back( *item );
991 }
992
993 selectionTool->AddItemsToSel( &toAdd );
994 frame()->Refresh();
995
996 SelectionResponse response;
997
998 for( EDA_ITEM* item : selectionTool->GetSelection() )
999 item->Serialize( *response.add_items() );
1000
1001 return response;
1002}
1003
1004
1007{
1008 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RemoveFromSelection" ) )
1009 return tl::unexpected( *headless );
1010
1011 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1012 return tl::unexpected( *busy );
1013
1014 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1015 {
1016 ApiResponseStatus e;
1017 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
1018 e.set_status( ApiStatusCode::AS_UNHANDLED );
1019 return tl::unexpected( e );
1020 }
1021
1022 TOOL_MANAGER* mgr = toolManager();
1023 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
1024
1025 std::vector<EDA_ITEM*> toRemove;
1026
1027 for( const types::KIID& id : aCtx.Request.items() )
1028 {
1029 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
1030 toRemove.emplace_back( *item );
1031 }
1032
1033 selectionTool->RemoveItemsFromSel( &toRemove );
1034 frame()->Refresh();
1035
1036 SelectionResponse response;
1037
1038 for( EDA_ITEM* item : selectionTool->GetSelection() )
1039 item->Serialize( *response.add_items() );
1040
1041 return response;
1042}
1043
1044
1047{
1048 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1049
1050 if( !documentValidation )
1051 return tl::unexpected( documentValidation.error() );
1052
1053 BoardStackupResponse response;
1054 google::protobuf::Any any;
1055
1057
1058 any.UnpackTo( response.mutable_stackup() );
1059
1060 // User-settable layer names are not stored in BOARD_STACKUP at the moment
1061 for( board::BoardStackupLayer& layer : *response.mutable_stackup()->mutable_layers() )
1062 {
1063 if( layer.type() == board::BoardStackupLayerType::BSLT_DIELECTRIC )
1064 continue;
1065
1066 PCB_LAYER_ID id = FromProtoEnum<PCB_LAYER_ID>( layer.layer() );
1067 wxCHECK2( id != UNDEFINED_LAYER, continue );
1068
1069 layer.set_user_name( board()->GetLayerName( id ) );
1070 }
1071
1072 return response;
1073}
1074
1075
1078{
1079 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1080
1081 if( !documentValidation )
1082 return tl::unexpected( documentValidation.error() );
1083
1084 BoardEnabledLayersResponse response;
1085
1086 BOARD* board = this->board();
1087 int copperLayerCount = board->GetCopperLayerCount();
1088
1089 response.set_copper_layer_count( copperLayerCount );
1090
1091 LSET enabled = board->GetEnabledLayers();
1092
1093 // The Rescue layer is an internal detail and should be hidden from the API
1094 enabled.reset( Rescue );
1095
1096 // Just in case this is out of sync; the API should always return the expected copper layers
1097 enabled |= LSET::AllCuMask( copperLayerCount );
1098
1099 board::PackLayerSet( *response.mutable_layers(), enabled );
1100
1101 return response;
1102}
1103
1104
1107{
1108 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1109
1110 if( !documentValidation )
1111 return tl::unexpected( documentValidation.error() );
1112
1113 if( aCtx.Request.copper_layer_count() % 2 != 0 )
1114 {
1115 ApiResponseStatus e;
1116 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1117 e.set_error_message( "copper_layer_count must be an even number" );
1118 return tl::unexpected( e );
1119 }
1120
1121 if( aCtx.Request.copper_layer_count() > MAX_CU_LAYERS )
1122 {
1123 ApiResponseStatus e;
1124 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1125 e.set_error_message( fmt::format( "copper_layer_count must be below %d", MAX_CU_LAYERS ) );
1126 return tl::unexpected( e );
1127 }
1128
1129 int copperLayerCount = static_cast<int>( aCtx.Request.copper_layer_count() );
1130 LSET enabled = board::UnpackLayerSet( aCtx.Request.layers() );
1131
1132 // Sanitize the input
1133 enabled |= LSET( { Edge_Cuts, Margin, F_CrtYd, B_CrtYd } );
1134 enabled &= ~LSET::AllCuMask();
1135 enabled |= LSET::AllCuMask( copperLayerCount );
1136
1137 BOARD* board = this->board();
1138
1139 LSET previousEnabled = board->GetEnabledLayers();
1140 LSET changedLayers = enabled ^ previousEnabled;
1141
1142 board->SetEnabledLayers( enabled );
1143 board->SetVisibleLayers( board->GetVisibleLayers() | changedLayers );
1144
1145 LSEQ removedLayers;
1146
1147 for( PCB_LAYER_ID layer_id : previousEnabled )
1148 {
1149 if( !enabled[layer_id] && board->HasItemsOnLayer( layer_id ) )
1150 removedLayers.push_back( layer_id );
1151 }
1152
1153 bool modified = false;
1154
1155 if( !removedLayers.empty() )
1156 {
1158
1159 for( PCB_LAYER_ID layer_id : removedLayers )
1160 modified |= board->RemoveAllItemsOnLayer( layer_id );
1161 }
1162
1163 if( enabled != previousEnabled )
1165
1166 if( modified )
1167 frame()->OnModify();
1168
1169 BoardEnabledLayersResponse response;
1170
1171 response.set_copper_layer_count( copperLayerCount );
1172 board::PackLayerSet( *response.mutable_layers(), enabled );
1173
1174 return response;
1175}
1176
1177
1180{
1181 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1182
1183 if( !documentValidation )
1184 return tl::unexpected( documentValidation.error() );
1185
1187 GraphicsDefaultsResponse response;
1188
1189 // TODO: This should change to be an enum class
1190 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
1191 kiapi::board::BLC_SILKSCREEN,
1192 kiapi::board::BLC_COPPER,
1193 kiapi::board::BLC_EDGES,
1194 kiapi::board::BLC_COURTYARD,
1195 kiapi::board::BLC_FABRICATION,
1196 kiapi::board::BLC_OTHER
1197 };
1198
1199 for( int i = 0; i < LAYER_CLASS_COUNT; ++i )
1200 {
1201 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
1202
1203 l->set_layer( classOrder[i] );
1204 l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] );
1205
1206 kiapi::common::types::TextAttributes* text = l->mutable_text();
1207 text->mutable_size()->set_x_nm( bds.m_TextSize[i].x );
1208 text->mutable_size()->set_y_nm( bds.m_TextSize[i].y );
1209 text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] );
1210 text->set_italic( bds.m_TextItalic[i] );
1211 text->set_keep_upright( bds.m_TextUpright[i] );
1212 }
1213
1214 return response;
1215}
1216
1217
1220{
1221 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1222
1223 if( !documentValidation )
1224 return tl::unexpected( documentValidation.error() );
1225
1227 BoardDesignRulesResponse response;
1228 kiapi::board::BoardDesignRules* rules = response.mutable_rules();
1229
1230 kiapi::board::MinimumConstraints* constraints = rules->mutable_constraints();
1231
1232 constraints->mutable_min_clearance()->set_value_nm( bds.m_MinClearance );
1233 constraints->mutable_min_groove_width()->set_value_nm( bds.m_MinGrooveWidth );
1234 constraints->mutable_min_connection_width()->set_value_nm( bds.m_MinConn );
1235 constraints->mutable_min_track_width()->set_value_nm( bds.m_TrackMinWidth );
1236 constraints->mutable_min_via_annular_width()->set_value_nm( bds.m_ViasMinAnnularWidth );
1237 constraints->mutable_min_via_size()->set_value_nm( bds.m_ViasMinSize );
1238 constraints->mutable_min_through_drill()->set_value_nm( bds.m_MinThroughDrill );
1239 constraints->mutable_min_microvia_size()->set_value_nm( bds.m_MicroViasMinSize );
1240 constraints->mutable_min_microvia_drill()->set_value_nm( bds.m_MicroViasMinDrill );
1241 constraints->mutable_copper_edge_clearance()->set_value_nm( bds.m_CopperEdgeClearance );
1242 constraints->mutable_hole_clearance()->set_value_nm( bds.m_HoleClearance );
1243 constraints->mutable_hole_to_hole_min()->set_value_nm( bds.m_HoleToHoleMin );
1244 constraints->mutable_silk_clearance()->set_value_nm( bds.m_SilkClearance );
1245 constraints->set_min_resolved_spokes( bds.m_MinResolvedSpokes );
1246 constraints->mutable_min_silk_text_height()->set_value_nm( bds.m_MinSilkTextHeight );
1247 constraints->mutable_min_silk_text_thickness()->set_value_nm( bds.m_MinSilkTextThickness );
1248
1249 kiapi::board::PredefinedSizes* sizes = rules->mutable_predefined_sizes();
1250
1251 for( size_t ii = 1; ii < bds.m_TrackWidthList.size(); ++ii )
1252 sizes->add_tracks()->mutable_width()->set_value_nm( bds.m_TrackWidthList[ii] );
1253
1254 for( size_t ii = 1; ii < bds.m_ViasDimensionsList.size(); ++ii )
1255 {
1256 kiapi::board::PresetViaDimension* via = sizes->add_vias();
1257 via->mutable_diameter()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Diameter );
1258 via->mutable_drill()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Drill );
1259 }
1260
1261 for( size_t ii = 1; ii < bds.m_DiffPairDimensionsList.size(); ++ii )
1262 {
1263 kiapi::board::PresetDiffPairDimension* pair = sizes->add_diff_pairs();
1264 pair->mutable_width()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Width );
1265 pair->mutable_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Gap );
1266 pair->mutable_via_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_ViaGap );
1267 }
1268
1269 kiapi::board::SolderMaskPasteDefaults* maskPaste = rules->mutable_solder_mask_paste();
1270
1271 maskPaste->mutable_mask_expansion()->set_value_nm( bds.m_SolderMaskExpansion );
1272 maskPaste->mutable_mask_min_width()->set_value_nm( bds.m_SolderMaskMinWidth );
1273 maskPaste->mutable_mask_to_copper_clearance()->set_value_nm( bds.m_SolderMaskToCopperClearance );
1274 maskPaste->mutable_paste_margin()->set_value_nm( bds.m_SolderPasteMargin );
1275 maskPaste->set_paste_margin_ratio( bds.m_SolderPasteMarginRatio );
1276 maskPaste->set_allow_soldermask_bridges_in_footprints( bds.m_AllowSoldermaskBridgesInFPs );
1277
1278 kiapi::board::TeardropDefaults* teardrops = rules->mutable_teardrops();
1279
1280 teardrops->set_target_vias( bds.m_TeardropParamsList.m_TargetVias );
1281 teardrops->set_target_pth_pads( bds.m_TeardropParamsList.m_TargetPTHPads );
1282 teardrops->set_target_smd_pads( bds.m_TeardropParamsList.m_TargetSMDPads );
1283 teardrops->set_target_track_to_track( bds.m_TeardropParamsList.m_TargetTrack2Track );
1284 teardrops->set_use_round_shapes_only( bds.m_TeardropParamsList.m_UseRoundShapesOnly );
1285
1287
1288 for( int target = TARGET_ROUND; target <= TARGET_TRACK; ++target )
1289 {
1290 const TEARDROP_PARAMETERS* params = tdList.GetParameters( static_cast<TARGET_TD>( target ) );
1291 kiapi::board::TeardropTargetEntry* entry = teardrops->add_target_params();
1292
1294 static_cast<TARGET_TD>( target ) ) );
1295 entry->mutable_params()->set_enabled( params->m_Enabled );
1296 entry->mutable_params()->mutable_max_length()->set_value_nm( params->m_TdMaxLen );
1297 entry->mutable_params()->mutable_max_width()->set_value_nm( params->m_TdMaxWidth );
1298 entry->mutable_params()->set_best_length_ratio( params->m_BestLengthRatio );
1299 entry->mutable_params()->set_best_width_ratio( params->m_BestWidthRatio );
1300 entry->mutable_params()->set_width_to_size_filter_ratio( params->m_WidthtoSizeFilterRatio );
1301 entry->mutable_params()->set_curved_edges( params->m_CurvedEdges );
1302 entry->mutable_params()->set_allow_two_tracks( params->m_AllowUseTwoTracks );
1303 entry->mutable_params()->set_on_pads_in_zones( params->m_TdOnPadsInZones );
1304 }
1305
1306 kiapi::board::ViaProtectionDefaults* viaProtection = rules->mutable_via_protection();
1307
1308 viaProtection->set_tent_front( bds.m_TentViasFront );
1309 viaProtection->set_tent_back( bds.m_TentViasBack );
1310 viaProtection->set_cover_front( bds.m_CoverViasFront );
1311 viaProtection->set_cover_back( bds.m_CoverViasBack );
1312 viaProtection->set_plug_front( bds.m_PlugViasFront );
1313 viaProtection->set_plug_back( bds.m_PlugViasBack );
1314 viaProtection->set_cap( bds.m_CapVias );
1315 viaProtection->set_fill( bds.m_FillVias );
1316
1317 for( const auto& [errorCode, severity] : bds.m_DRCSeverities )
1318 {
1319 board::DrcSeveritySetting* setting = rules->add_severities();
1320 setting->set_rule_type(
1321 ToProtoEnum<PCB_DRC_CODE, board::DrcErrorType>( static_cast<PCB_DRC_CODE>( errorCode ) ) );
1322 setting->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( severity ) );
1323 }
1324
1325 for( const wxString& serialized : bds.m_DrcExclusions )
1326 {
1327 kiapi::board::DrcExclusion* exclusion = rules->add_exclusions();
1328 exclusion->mutable_marker()->mutable_id()->set_opaque_id( serialized.ToStdString() );
1329
1330 auto it = bds.m_DrcExclusionComments.find( serialized );
1331
1332 if( it != bds.m_DrcExclusionComments.end() )
1333 exclusion->set_comment( it->second.ToStdString() );
1334 }
1335
1336 response.set_custom_rules_status( CRS_NONE );
1337
1338 wxFileName fn = context()->GetBoard()->GetFileName();
1340 wxString rulesPath = context()->Prj().AbsolutePath( fn.GetFullName() );
1341
1342 if( !rulesPath.IsEmpty() && wxFileName::IsFileReadable( rulesPath ) )
1343 {
1344 wxFFile file( rulesPath, "r" );
1345 wxString content;
1346 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1347
1348 if( !file.IsOpened() )
1349 {
1350 response.set_custom_rules_status( CRS_INVALID );
1351 return response;
1352 }
1353
1354 file.ReadAll( &content );
1355 file.Close();
1356
1357 try
1358 {
1359 DRC_RULES_PARSER parser( content, "File" );
1360 parser.Parse( parsedRules, nullptr );
1361 response.set_custom_rules_status( CRS_VALID );
1362 }
1363 catch( const IO_ERROR& )
1364 {
1365 response.set_custom_rules_status( CRS_INVALID );
1366 }
1367 }
1368
1369 return response;
1370}
1371
1372
1375{
1376 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1377
1378 if( !documentValidation )
1379 return tl::unexpected( documentValidation.error() );
1380
1381 BOARD_DESIGN_SETTINGS newSettings( board()->GetDesignSettings() );
1382 const kiapi::board::BoardDesignRules& rules = aCtx.Request.rules();
1383
1384 if( rules.has_constraints() )
1385 {
1386 const kiapi::board::MinimumConstraints& constraints = rules.constraints();
1387
1388 newSettings.m_MinClearance = constraints.min_clearance().value_nm();
1389 newSettings.m_MinGrooveWidth = constraints.min_groove_width().value_nm();
1390 newSettings.m_MinConn = constraints.min_connection_width().value_nm();
1391 newSettings.m_TrackMinWidth = constraints.min_track_width().value_nm();
1392 newSettings.m_ViasMinAnnularWidth = constraints.min_via_annular_width().value_nm();
1393 newSettings.m_ViasMinSize = constraints.min_via_size().value_nm();
1394 newSettings.m_MinThroughDrill = constraints.min_through_drill().value_nm();
1395 newSettings.m_MicroViasMinSize = constraints.min_microvia_size().value_nm();
1396 newSettings.m_MicroViasMinDrill = constraints.min_microvia_drill().value_nm();
1397 newSettings.m_CopperEdgeClearance = constraints.copper_edge_clearance().value_nm();
1398 newSettings.m_HoleClearance = constraints.hole_clearance().value_nm();
1399 newSettings.m_HoleToHoleMin = constraints.hole_to_hole_min().value_nm();
1400 newSettings.m_SilkClearance = constraints.silk_clearance().value_nm();
1401 newSettings.m_MinResolvedSpokes = constraints.min_resolved_spokes();
1402 newSettings.m_MinSilkTextHeight = constraints.min_silk_text_height().value_nm();
1403 newSettings.m_MinSilkTextThickness = constraints.min_silk_text_thickness().value_nm();
1404 }
1405
1406 if( rules.has_predefined_sizes() )
1407 {
1408 newSettings.m_TrackWidthList.clear();
1409 newSettings.m_TrackWidthList.emplace_back( 0 );
1410
1411 for( const kiapi::board::PresetTrackWidth& track : rules.predefined_sizes().tracks() )
1412 newSettings.m_TrackWidthList.emplace_back( track.width().value_nm() );
1413
1414 newSettings.m_ViasDimensionsList.clear();
1415 newSettings.m_ViasDimensionsList.emplace_back( 0, 0 );
1416
1417 for( const kiapi::board::PresetViaDimension& via : rules.predefined_sizes().vias() )
1418 {
1419 newSettings.m_ViasDimensionsList.emplace_back( static_cast<int>( via.diameter().value_nm() ),
1420 static_cast<int>( via.drill().value_nm() ) );
1421 }
1422
1423 newSettings.m_DiffPairDimensionsList.clear();
1424 newSettings.m_DiffPairDimensionsList.emplace_back( 0, 0, 0 );
1425
1426 for( const kiapi::board::PresetDiffPairDimension& pair : rules.predefined_sizes().diff_pairs() )
1427 {
1428 newSettings.m_DiffPairDimensionsList.emplace_back(
1429 static_cast<int>( pair.width().value_nm() ),
1430 static_cast<int>( pair.gap().value_nm() ),
1431 static_cast<int>( pair.via_gap().value_nm() ) );
1432 }
1433 }
1434
1435 if( rules.has_solder_mask_paste() )
1436 {
1437 const kiapi::board::SolderMaskPasteDefaults& maskPaste = rules.solder_mask_paste();
1438
1439 newSettings.m_SolderMaskExpansion = maskPaste.mask_expansion().value_nm();
1440 newSettings.m_SolderMaskMinWidth = maskPaste.mask_min_width().value_nm();
1441 newSettings.m_SolderMaskToCopperClearance = maskPaste.mask_to_copper_clearance().value_nm();
1442 newSettings.m_SolderPasteMargin = maskPaste.paste_margin().value_nm();
1443 newSettings.m_SolderPasteMarginRatio = maskPaste.paste_margin_ratio();
1444 newSettings.m_AllowSoldermaskBridgesInFPs =
1445 maskPaste.allow_soldermask_bridges_in_footprints();
1446 }
1447
1448 if( rules.has_teardrops() )
1449 {
1450 const kiapi::board::TeardropDefaults& teardrops = rules.teardrops();
1451
1452 newSettings.m_TeardropParamsList.m_TargetVias = teardrops.target_vias();
1453 newSettings.m_TeardropParamsList.m_TargetPTHPads = teardrops.target_pth_pads();
1454 newSettings.m_TeardropParamsList.m_TargetSMDPads = teardrops.target_smd_pads();
1455 newSettings.m_TeardropParamsList.m_TargetTrack2Track = teardrops.target_track_to_track();
1456 newSettings.m_TeardropParamsList.m_UseRoundShapesOnly = teardrops.use_round_shapes_only();
1457
1458 for( const kiapi::board::TeardropTargetEntry& entry : teardrops.target_params() )
1459 {
1460 if( entry.target() == kiapi::board::TeardropTarget::TDT_UNKNOWN )
1461 continue;
1462
1464 entry.target() );
1465
1466 TEARDROP_PARAMETERS* params = newSettings.m_TeardropParamsList.GetParameters( target );
1467
1468 params->m_Enabled = entry.params().enabled();
1469 params->m_TdMaxLen = entry.params().max_length().value_nm();
1470 params->m_TdMaxWidth = entry.params().max_width().value_nm();
1471 params->m_BestLengthRatio = entry.params().best_length_ratio();
1472 params->m_BestWidthRatio = entry.params().best_width_ratio();
1473 params->m_WidthtoSizeFilterRatio = entry.params().width_to_size_filter_ratio();
1474 params->m_CurvedEdges = entry.params().curved_edges();
1475 params->m_AllowUseTwoTracks = entry.params().allow_two_tracks();
1476 params->m_TdOnPadsInZones = entry.params().on_pads_in_zones();
1477 }
1478 }
1479
1480 if( rules.has_via_protection() )
1481 {
1482 const kiapi::board::ViaProtectionDefaults& viaProtection = rules.via_protection();
1483
1484 newSettings.m_TentViasFront = viaProtection.tent_front();
1485 newSettings.m_TentViasBack = viaProtection.tent_back();
1486 newSettings.m_CoverViasFront = viaProtection.cover_front();
1487 newSettings.m_CoverViasBack = viaProtection.cover_back();
1488 newSettings.m_PlugViasFront = viaProtection.plug_front();
1489 newSettings.m_PlugViasBack = viaProtection.plug_back();
1490 newSettings.m_CapVias = viaProtection.cap();
1491 newSettings.m_FillVias = viaProtection.fill();
1492 }
1493
1494 if( rules.severities_size() > 0 )
1495 {
1496 newSettings.m_DRCSeverities.clear();
1497
1498 for( const kiapi::board::DrcSeveritySetting& severitySetting : rules.severities() )
1499 {
1500 PCB_DRC_CODE ruleType =
1501 FromProtoEnum<PCB_DRC_CODE, kiapi::board::DrcErrorType>( severitySetting.rule_type() );
1502
1503 const std::unordered_set<SEVERITY> permitted( { RPT_SEVERITY_ERROR, RPT_SEVERITY_WARNING, RPT_SEVERITY_IGNORE } );
1504 SEVERITY setting = FromProtoEnum<SEVERITY, kiapi::common::types::RuleSeverity>( severitySetting.severity() );
1505
1506 if( !permitted.contains( setting ) )
1507 {
1508 ApiResponseStatus e;
1509 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1510 e.set_error_message( fmt::format( "DRC severity must be error, warning, or ignore" ) );
1511 return tl::unexpected( e );
1512 }
1513
1514 newSettings.m_DRCSeverities[ruleType] = setting;
1515 }
1516 }
1517
1518 if( rules.exclusions_size() > 0 )
1519 {
1520 newSettings.m_DrcExclusions.clear();
1521 newSettings.m_DrcExclusionComments.clear();
1522
1523 for( const kiapi::board::DrcExclusion& exclusion : rules.exclusions() )
1524 {
1525 wxString serialized = wxString::FromUTF8( exclusion.marker().id().opaque_id() );
1526
1527 if( serialized.IsEmpty() )
1528 {
1529 ApiResponseStatus e;
1530 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1531 e.set_error_message( "DrcExclusion marker id must not be empty" );
1532 return tl::unexpected( e );
1533 }
1534
1535 newSettings.m_DrcExclusions.insert( serialized );
1536 newSettings.m_DrcExclusionComments[serialized] = wxString::FromUTF8( exclusion.comment() );
1537 }
1538 }
1539
1540 std::vector<BOARD_DESIGN_SETTINGS::VALIDATION_ERROR> errors = newSettings.ValidateDesignRules();
1541
1542 if( !errors.empty() )
1543 {
1544 const BOARD_DESIGN_SETTINGS::VALIDATION_ERROR& error = errors.front();
1545
1546 ApiResponseStatus e;
1547 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1548 e.set_error_message( fmt::format( "Invalid board design rules: {}: {}",
1549 error.setting_name.ToStdString(),
1550 error.error_message.ToStdString() ) );
1551 return tl::unexpected( e );
1552 }
1553
1554 board()->SetDesignSettings( newSettings );
1555
1556 if( frame() )
1557 {
1558 frame()->OnModify();
1560 }
1561
1562 HANDLER_CONTEXT<GetBoardDesignRules> getCtx = { aCtx.ClientName, GetBoardDesignRules() };
1563 *getCtx.Request.mutable_board() = aCtx.Request.board();
1564
1565 return handleGetBoardDesignRules( getCtx );
1566}
1567
1568
1571{
1572 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1573
1574 if( !documentValidation )
1575 return tl::unexpected( documentValidation.error() );
1576
1577 CustomRulesResponse response;
1578 response.set_status( CRS_NONE );
1579
1580 wxFileName fn = context()->GetBoard()->GetFileName();
1582 wxString rulesPath = context()->Prj().AbsolutePath( fn.GetFullName() );
1583
1584 if( rulesPath.IsEmpty() || !wxFileName::IsFileReadable( rulesPath ) )
1585 return response;
1586
1587 wxFFile file( rulesPath, "r" );
1588
1589 if( !file.IsOpened() )
1590 {
1591 response.set_status( CRS_INVALID );
1592 response.set_error_text( "Failed to open custom rules file" );
1593 return response;
1594 }
1595
1596 wxString content;
1597 file.ReadAll( &content );
1598 file.Close();
1599
1600 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1601
1602 try
1603 {
1604 DRC_RULES_PARSER parser( content, "File" );
1605 parser.Parse( parsedRules, nullptr );
1606 }
1607 catch( const IO_ERROR& ioe )
1608 {
1609 response.set_status( CRS_INVALID );
1610 response.set_error_text( ioe.What().ToStdString() );
1611 return response;
1612 }
1613
1614 for( const std::shared_ptr<DRC_RULE>& rule : parsedRules )
1615 {
1616 // TODO(JE) since we now need this for both here and the rules editor, maybe it's time
1617 // to just make comment parsing part of the parser?
1618 wxString text = DRC_RULE_LOADER::ExtractRuleText( content, rule->m_Name );
1619 wxString comment = DRC_RULE_LOADER::ExtractRuleComment( text );
1620
1621 kiapi::board::CustomRule* customRule = response.add_rules();
1622
1623 if( rule->m_Condition )
1624 customRule->set_condition( rule->m_Condition->GetExpression().ToUTF8() );
1625
1626 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
1627 {
1628 board::CustomRuleConstraint* constraintProto = customRule->add_constraints();
1629 constraint.ToProto( *constraintProto );
1630 }
1631
1632 customRule->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( rule->m_Severity ) );
1633 customRule->set_name( rule->m_Name.ToUTF8() );
1634
1635 if( rule->m_LayerSource.CmpNoCase( wxS( "outer" ) ) == 0 )
1636 {
1637 customRule->set_layer_mode( kiapi::board::CRLM_OUTER );
1638 }
1639 else if( rule->m_LayerSource.CmpNoCase( wxS( "inner" ) ) == 0 )
1640 {
1641 customRule->set_layer_mode( kiapi::board::CRLM_INNER );
1642 }
1643 else if( !rule->m_LayerSource.IsEmpty() )
1644 {
1645 int layer = LSET::NameToLayer( rule->m_LayerSource );
1646
1647 if( layer != UNDEFINED_LAYER && layer != UNSELECTED_LAYER && layer < PCB_LAYER_ID_COUNT )
1648 {
1649 customRule->set_single_layer(
1651 }
1652 }
1653
1654 if( !comment.IsEmpty() )
1655 customRule->set_comments( comment );
1656 }
1657
1658 response.set_status( CRS_VALID );
1659 return response;
1660}
1661
1662
1665{
1666 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1667
1668 if( !documentValidation )
1669 return tl::unexpected( documentValidation.error() );
1670
1671 wxFileName fn = context()->GetBoard()->GetFileName();
1673 wxString rulesPath = context()->Prj().AbsolutePath( fn.GetFullName() );
1674
1675 if( aCtx.Request.rules_size() == 0 )
1676 {
1677 if( wxFileName::FileExists( rulesPath ) )
1678 {
1679 if( !wxRemoveFile( rulesPath ) )
1680 {
1681 CustomRulesResponse response;
1682 response.set_status( CRS_INVALID );
1683 response.set_error_text( "Failed to remove custom rules file" );
1684 return response;
1685 }
1686 }
1687
1688 CustomRulesResponse response;
1689 response.set_status( CRS_NONE );
1690 return response;
1691 }
1692
1693 wxString rulesText;
1694 rulesText << "(version 1)\n";
1695
1696 for( const board::CustomRule& rule : aCtx.Request.rules() )
1697 {
1698 wxString serializationError;
1699 wxString serializedRule = DRC_RULE::FormatRuleFromProto( rule, &serializationError );
1700
1701 if( serializedRule.IsEmpty() )
1702 {
1703 CustomRulesResponse response;
1704 response.set_status( CRS_INVALID );
1705
1706 if( serializationError.IsEmpty() )
1707 response.set_error_text( "Failed to serialize custom rule" );
1708 else
1709 response.set_error_text( serializationError.ToUTF8() );
1710
1711 return response;
1712 }
1713
1714 rulesText << "\n" << serializedRule;
1715 }
1716
1717 // Validate generated file text before writing so callers get parser errors in response.
1718 try
1719 {
1720 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1721 DRC_RULES_PARSER parser( rulesText, "SetCustomDesignRules" );
1722 parser.Parse( parsedRules, nullptr );
1723 }
1724 catch( const IO_ERROR& ioe )
1725 {
1726 CustomRulesResponse response;
1727 response.set_status( CRS_INVALID );
1728 response.set_error_text( ioe.What().ToStdString() );
1729 return response;
1730 }
1731
1732 wxFFile file( rulesPath, "w" );
1733
1734 if( !file.IsOpened() )
1735 {
1736 CustomRulesResponse response;
1737 response.set_status( CRS_INVALID );
1738 response.set_error_text( "Failed to open custom rules file for writing" );
1739 return response;
1740 }
1741
1742 if( !file.Write( rulesText ) )
1743 {
1744 file.Close();
1745
1746 CustomRulesResponse response;
1747 response.set_status( CRS_INVALID );
1748 response.set_error_text( "Failed to write custom rules file" );
1749 return response;
1750 }
1751
1752 file.Close();
1753
1754 HANDLER_CONTEXT<GetCustomDesignRules> getCtx = { aCtx.ClientName, GetCustomDesignRules() };
1755 *getCtx.Request.mutable_board() = aCtx.Request.board();
1756 return handleGetCustomDesignRules( getCtx );
1757}
1758
1759
1762{
1763 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1764 !documentValidation )
1765 {
1766 return tl::unexpected( documentValidation.error() );
1767 }
1768
1769 VECTOR2I origin;
1770 const BOARD_DESIGN_SETTINGS& settings = board()->GetDesignSettings();
1771
1772 switch( aCtx.Request.type() )
1773 {
1774 case BOT_GRID:
1775 origin = settings.GetGridOrigin();
1776 break;
1777
1778 case BOT_DRILL:
1779 origin = settings.GetAuxOrigin();
1780 break;
1781
1782 default:
1783 case BOT_UNKNOWN:
1784 {
1785 ApiResponseStatus e;
1786 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1787 e.set_error_message( "Unexpected origin type" );
1788 return tl::unexpected( e );
1789 }
1790 }
1791
1792 types::Vector2 reply;
1793 PackVector2( reply, origin );
1794 return reply;
1795}
1796
1799{
1800 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1801 return tl::unexpected( *busy );
1802
1803 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1804 !documentValidation )
1805 {
1806 return tl::unexpected( documentValidation.error() );
1807 }
1808
1809 VECTOR2I origin = UnpackVector2( aCtx.Request.origin() );
1810
1811 switch( aCtx.Request.type() )
1812 {
1813 case BOT_GRID:
1814 {
1815 PCB_EDIT_FRAME* f = frame();
1816
1817 frame()->CallAfter( [f, origin]()
1818 {
1819 // gridSetOrigin takes ownership and frees this
1820 VECTOR2D* dorigin = new VECTOR2D( origin );
1821 TOOL_MANAGER* mgr = f->GetToolManager();
1822 mgr->RunAction( PCB_ACTIONS::gridSetOrigin, dorigin );
1823 f->Refresh();
1824 } );
1825 break;
1826 }
1827
1828 case BOT_DRILL:
1829 {
1830 PCB_EDIT_FRAME* f = frame();
1831
1832 frame()->CallAfter( [f, origin]()
1833 {
1834 TOOL_MANAGER* mgr = f->GetToolManager();
1835 mgr->RunAction( PCB_ACTIONS::drillSetOrigin, origin );
1836 f->Refresh();
1837 } );
1838 break;
1839 }
1840
1841 default:
1842 case BOT_UNKNOWN:
1843 {
1844 ApiResponseStatus e;
1845 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1846 e.set_error_message( "Unexpected origin type" );
1847 return tl::unexpected( e );
1848 }
1849 }
1850
1851 return Empty();
1852}
1853
1854
1857{
1858 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1859 !documentValidation )
1860 {
1861 return tl::unexpected( documentValidation.error() );
1862 }
1863
1864 BoardLayerNameResponse response;
1865
1867
1868 response.set_name( board()->GetLayerName( id ) );
1869
1870 return response;
1871}
1872
1873
1876{
1877 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1878 return tl::unexpected( *busy );
1879
1880 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1881 {
1882 ApiResponseStatus e;
1883 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
1884 e.set_status( ApiStatusCode::AS_UNHANDLED );
1885 return tl::unexpected( e );
1886 }
1887
1888 GetBoundingBoxResponse response;
1889 bool includeText = aCtx.Request.mode() == BoundingBoxMode::BBM_ITEM_AND_CHILD_TEXT;
1890
1891 for( const types::KIID& idMsg : aCtx.Request.items() )
1892 {
1893 KIID id( idMsg.value() );
1894 std::optional<BOARD_ITEM*> optItem = getItemById( id );
1895
1896 if( !optItem )
1897 continue;
1898
1899 BOARD_ITEM* item = *optItem;
1900 BOX2I bbox;
1901
1902 if( item->Type() == PCB_FOOTPRINT_T )
1903 bbox = static_cast<FOOTPRINT*>( item )->GetBoundingBox( includeText );
1904 else
1905 bbox = item->GetBoundingBox();
1906
1907 response.add_items()->set_value( idMsg.value() );
1908 PackBox2( *response.add_boxes(), bbox );
1909 }
1910
1911 return response;
1912}
1913
1914
1917{
1918 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1919 !documentValidation )
1920 {
1921 return tl::unexpected( documentValidation.error() );
1922 }
1923
1924 PadShapeAsPolygonResponse response;
1926
1927 for( const types::KIID& padRequest : aCtx.Request.pads() )
1928 {
1929 KIID id( padRequest.value() );
1930 std::optional<BOARD_ITEM*> optPad = getItemById( id );
1931
1932 if( !optPad || ( *optPad )->Type() != PCB_PAD_T )
1933 continue;
1934
1935 response.add_pads()->set_value( padRequest.value() );
1936
1937 PAD* pad = static_cast<PAD*>( *optPad );
1938 SHAPE_POLY_SET poly;
1939 pad->TransformShapeToPolygon( poly, pad->Padstack().EffectiveLayerFor( layer ), 0,
1940 pad->GetMaxError(), ERROR_INSIDE );
1941
1942 types::PolygonWithHoles* polyMsg = response.mutable_polygons()->Add();
1943 PackPolyLine( *polyMsg->mutable_outline(), poly.COutline( 0 ) );
1944 }
1945
1946 return response;
1947}
1948
1949
1952{
1953 using board::types::BoardLayer;
1954
1955 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1956 !documentValidation )
1957 {
1958 return tl::unexpected( documentValidation.error() );
1959 }
1960
1961 PadstackPresenceResponse response;
1962
1963 LSET layers;
1964
1965 for( const int layer : aCtx.Request.layers() )
1966 layers.set( FromProtoEnum<PCB_LAYER_ID, BoardLayer>( static_cast<BoardLayer>( layer ) ) );
1967
1968 for( const types::KIID& padRequest : aCtx.Request.items() )
1969 {
1970 KIID id( padRequest.value() );
1971 std::optional<BOARD_ITEM*> optItem = getItemById( id );
1972
1973 if( !optItem )
1974 continue;
1975
1976 switch( ( *optItem )->Type() )
1977 {
1978 case PCB_PAD_T:
1979 {
1980 PAD* pad = static_cast<PAD*>( *optItem );
1981
1982 for( PCB_LAYER_ID layer : layers )
1983 {
1984 PadstackPresenceEntry* entry = response.add_entries();
1985 entry->mutable_item()->set_value( pad->m_Uuid.AsStdString() );
1986 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
1987 entry->set_presence( pad->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
1988 }
1989
1990 break;
1991 }
1992
1993 case PCB_VIA_T:
1994 {
1995 PCB_VIA* via = static_cast<PCB_VIA*>( *optItem );
1996
1997 for( PCB_LAYER_ID layer : layers )
1998 {
1999 PadstackPresenceEntry* entry = response.add_entries();
2000 entry->mutable_item()->set_value( via->m_Uuid.AsStdString() );
2001 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
2002 entry->set_presence( via->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
2003 }
2004
2005 break;
2006 }
2007
2008 default:
2009 break;
2010 }
2011 }
2012
2013 return response;
2014}
2015
2016
2017std::optional<TITLE_BLOCK*> API_HANDLER_PCB::getTitleBlock()
2018{
2019 return &context()->GetBoard()->GetTitleBlock();
2020}
2021
2022
2023std::optional<PAGE_INFO> API_HANDLER_PCB::getPageSettings()
2024{
2025 return context()->GetBoard()->GetPageSettings();
2026}
2027
2028
2030{
2031 context()->GetBoard()->SetPageSettings( aPageInfo );
2032 return true;
2033}
2034
2035
2040
2041
2042void API_HANDLER_PCB::setDrawingSheetFileName( const wxString& aFileName )
2043{
2045
2046 if( frame() )
2048}
2049
2050
2052{
2053 if( frame() )
2054 {
2055 frame()->Refresh();
2056 frame()->OnModify();
2058 }
2059}
2060
2061
2064{
2065 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2066
2067 if( !documentValidation )
2068 return tl::unexpected( documentValidation.error() );
2069
2070 ExpandTextVariablesResponse reply;
2071 BOARD* board = this->board();
2072
2073 std::function<bool( wxString* )> textResolver =
2074 [&]( wxString* token ) -> bool
2075 {
2076 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
2077 return board->ResolveTextVar( token, 0 );
2078 };
2079
2080 for( const std::string& textMsg : aCtx.Request.text() )
2081 {
2082 wxString text = ExpandTextVars( wxString::FromUTF8( textMsg ), &textResolver );
2083 reply.add_text( text.ToUTF8() );
2084 }
2085
2086 return reply;
2087}
2088
2089
2092{
2093 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "InteractiveMoveItems" ) )
2094 return tl::unexpected( *headless );
2095
2096 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2097 return tl::unexpected( *busy );
2098
2099 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2100
2101 if( !documentValidation )
2102 return tl::unexpected( documentValidation.error() );
2103
2104 TOOL_MANAGER* mgr = toolManager();
2105 std::vector<EDA_ITEM*> toSelect;
2106
2107 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
2108 {
2109 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
2110 toSelect.emplace_back( static_cast<EDA_ITEM*>( *item ) );
2111 }
2112
2113 if( toSelect.empty() )
2114 {
2115 ApiResponseStatus e;
2116 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2117 e.set_error_message( fmt::format( "None of the given items exist on the board",
2118 aCtx.Request.board().board_filename() ) );
2119 return tl::unexpected( e );
2120 }
2121
2122 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
2123 selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() );
2124
2126 mgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &toSelect );
2127
2128 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
2129 mgr->PostAPIAction( PCB_ACTIONS::move, commit );
2130
2131 return Empty();
2132}
2133
2134
2136{
2137 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2138
2139 if( !documentValidation )
2140 return tl::unexpected( documentValidation.error() );
2141
2142 NetsResponse response;
2143 BOARD* board = this->board();
2144
2145 std::set<wxString> netclassFilter;
2146
2147 for( const std::string& nc : aCtx.Request.netclass_filter() )
2148 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
2149
2150 for( NETINFO_ITEM* net : board->GetNetInfo() )
2151 {
2152 NETCLASS* nc = net->GetNetClass();
2153
2154 if( !netclassFilter.empty() && nc )
2155 {
2156 bool inClass = false;
2157
2158 for( const wxString& filter : netclassFilter )
2159 {
2160 if( nc->ContainsNetclassWithName( filter ) )
2161 {
2162 inClass = true;
2163 break;
2164 }
2165 }
2166
2167 if( !inClass )
2168 continue;
2169 }
2170
2171 board::types::Net* netProto = response.add_nets();
2172 netProto->set_name( net->GetNetname() );
2173 netProto->mutable_code()->set_value( net->GetNetCode() );
2174 }
2175
2176 return response;
2177}
2178
2179
2182{
2183 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2184 return tl::unexpected( *busy );
2185
2186 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
2187 {
2188 ApiResponseStatus e;
2189 e.set_status( ApiStatusCode::AS_UNHANDLED );
2190 return tl::unexpected( e );
2191 }
2192
2193 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
2194 const bool filterByType = aCtx.Request.types_size() > 0;
2195
2196 if( filterByType && types.empty() )
2197 {
2198 ApiResponseStatus e;
2199 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2200 e.set_error_message( "none of the requested types are valid for a Board object" );
2201 return tl::unexpected( e );
2202 }
2203
2204 std::set<KICAD_T> typeFilter( types.begin(), types.end() );
2205 std::vector<BOARD_CONNECTED_ITEM*> sourceItems;
2206
2207 for( const types::KIID& id : aCtx.Request.items() )
2208 {
2209 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
2210 {
2211 if( BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( *item ) )
2212 sourceItems.emplace_back( connected );
2213 }
2214 }
2215
2216 if( sourceItems.empty() )
2217 {
2218 ApiResponseStatus e;
2219 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2220 e.set_error_message( "none of the requested IDs were found or valid connected items" );
2221 return tl::unexpected( e );
2222 }
2223
2224 GetItemsResponse response;
2225 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2226 std::set<KIID> insertedItems;
2227
2228 for( BOARD_CONNECTED_ITEM* source : sourceItems )
2229 {
2230 for( BOARD_CONNECTED_ITEM* connected : conn->GetConnectedItems( source ) )
2231 {
2232 if( filterByType && !typeFilter.contains( connected->Type() ) )
2233 continue;
2234
2235 if( !insertedItems.insert( connected->m_Uuid ).second )
2236 continue;
2237
2238 connected->Serialize( *response.add_items() );
2239 }
2240 }
2241
2242 response.set_status( ItemRequestStatus::IRS_OK );
2243 return response;
2244}
2245
2246
2248 const HANDLER_CONTEXT<GetItemsByNet>& aCtx )
2249{
2250 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2251 return tl::unexpected( *busy );
2252
2253 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
2254 {
2255 ApiResponseStatus e;
2256 e.set_status( ApiStatusCode::AS_UNHANDLED );
2257 return tl::unexpected( e );
2258 }
2259
2260 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
2261 const bool filterByType = aCtx.Request.types_size() > 0;
2262
2263 if( filterByType && types.empty() )
2264 {
2265 ApiResponseStatus e;
2266 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2267 e.set_error_message( "none of the requested types are valid for a Board object" );
2268 return tl::unexpected( e );
2269 }
2270
2271 if( !filterByType )
2273
2274 GetItemsResponse response;
2275 BOARD* board = this->board();
2276 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
2277 std::set<KIID> insertedItems;
2278
2279 const NETINFO_LIST& nets = board->GetNetInfo();
2280
2281 for( const board::types::Net& net : aCtx.Request.nets() )
2282 {
2283 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
2284
2285 if( !netInfo )
2286 continue;
2287
2288 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( netInfo->GetNetCode(), types ) )
2289 {
2290 if( !insertedItems.insert( item->m_Uuid ).second )
2291 continue;
2292
2293 item->Serialize( *response.add_items() );
2294 }
2295 }
2296
2297 response.set_status( ItemRequestStatus::IRS_OK );
2298 return response;
2299}
2300
2301
2304{
2305 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2306 return tl::unexpected( *busy );
2307
2308 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
2309 {
2310 ApiResponseStatus e;
2311 e.set_status( ApiStatusCode::AS_UNHANDLED );
2312 return tl::unexpected( e );
2313 }
2314
2315 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
2316 const bool filterByType = aCtx.Request.types_size() > 0;
2317
2318 if( filterByType && types.empty() )
2319 {
2320 ApiResponseStatus e;
2321 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2322 e.set_error_message( "none of the requested types are valid for a Board object" );
2323 return tl::unexpected( e );
2324 }
2325
2326 if( !filterByType )
2328
2329 std::set<wxString> requestedClasses;
2330
2331 for( const std::string& netClass : aCtx.Request.net_classes() )
2332 requestedClasses.insert( wxString( netClass.c_str(), wxConvUTF8 ) );
2333
2334 GetItemsResponse response;
2335 BOARD* board = this->board();
2336 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
2337 std::set<KIID> insertedItems;
2338
2339 for( NETINFO_ITEM* net : board->GetNetInfo() )
2340 {
2341 if( !net )
2342 continue;
2343
2344 NETCLASS* nc = net->GetNetClass();
2345
2346 if( !requestedClasses.empty() )
2347 {
2348 if( !nc )
2349 continue;
2350
2351 bool inClass = false;
2352
2353 for( const wxString& filter : requestedClasses )
2354 {
2355 if( nc->ContainsNetclassWithName( filter ) )
2356 {
2357 inClass = true;
2358 break;
2359 }
2360 }
2361
2362 if( !inClass )
2363 continue;
2364 }
2365
2366 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( net->GetNetCode(), types ) )
2367 {
2368 if( !insertedItems.insert( item->m_Uuid ).second )
2369 continue;
2370
2371 item->Serialize( *response.add_items() );
2372 }
2373 }
2374
2375 response.set_status( ItemRequestStatus::IRS_OK );
2376 return response;
2377}
2378
2379
2382{
2383 NetClassForNetsResponse response;
2384
2385 BOARD* board = this->board();
2386 const NETINFO_LIST& nets = board->GetNetInfo();
2387 google::protobuf::Any any;
2388
2389 for( const board::types::Net& net : aCtx.Request.net() )
2390 {
2391 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
2392
2393 if( !netInfo )
2394 continue;
2395
2396 netInfo->GetNetClass()->Serialize( any );
2397 auto [pair, rc] = response.mutable_classes()->insert( { net.name(), {} } );
2398 any.UnpackTo( &pair->second );
2399 }
2400
2401 return response;
2402}
2403
2404
2406{
2407 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2408 return tl::unexpected( *busy );
2409
2410 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2411
2412 if( !documentValidation )
2413 return tl::unexpected( documentValidation.error() );
2414
2415 if( aCtx.Request.zones().empty() )
2416 {
2417 TOOL_MANAGER* mgr = toolManager();
2418 frame()->CallAfter( [mgr]()
2419 {
2421 } );
2422 }
2423 else
2424 {
2425 // TODO
2426 ApiResponseStatus e;
2427 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
2428 return tl::unexpected( e );
2429 }
2430
2431 return Empty();
2432}
2433
2434
2437{
2438 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2439
2440 if( !documentValidation )
2441 return tl::unexpected( documentValidation.error() );
2442
2443 SavedDocumentResponse response;
2444 response.mutable_document()->CopyFrom( aCtx.Request.document() );
2445
2446 CLIPBOARD_IO io;
2447 io.SetWriter(
2448 [&]( const wxString& aData )
2449 {
2450 response.set_contents( aData.ToUTF8() );
2451 } );
2452
2453 io.SaveBoard( wxEmptyString, board(), nullptr );
2454
2455 return response;
2456}
2457
2458
2461{
2462 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SaveSelectionToString" ) )
2463 return tl::unexpected( *headless );
2464
2465 SavedSelectionResponse response;
2466
2467 TOOL_MANAGER* mgr = toolManager();
2468 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
2469 PCB_SELECTION& selection = selectionTool->GetSelection();
2470
2471 CLIPBOARD_IO io;
2472 io.SetWriter(
2473 [&]( const wxString& aData )
2474 {
2475 response.set_contents( aData.ToUTF8() );
2476 } );
2477
2478 io.SetBoard( board() );
2479 io.SaveSelection( selection, false );
2480
2481 return response;
2482}
2483
2484
2487{
2488 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2489 return tl::unexpected( *busy );
2490
2491 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2492
2493 if( !documentValidation )
2494 return tl::unexpected( documentValidation.error() );
2495
2496 CreateItemsResponse response;
2497 return response;
2498}
2499
2500
2503{
2504 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetVisibleLayers" ) )
2505 return tl::unexpected( *headless );
2506
2507 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2508
2509 if( !documentValidation )
2510 return tl::unexpected( documentValidation.error() );
2511
2512 BoardLayers response;
2513
2514 for( PCB_LAYER_ID layer : board()->GetVisibleLayers() )
2515 response.add_layers( ToProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( layer ) );
2516
2517 return response;
2518}
2519
2520
2523{
2524 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetVisibleLayers" ) )
2525 return tl::unexpected( *headless );
2526
2527 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2528 return tl::unexpected( *busy );
2529
2530 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2531
2532 if( !documentValidation )
2533 return tl::unexpected( documentValidation.error() );
2534
2535 LSET visible;
2536 LSET enabled = board()->GetEnabledLayers();
2537
2538 for( int layerIdx : aCtx.Request.layers() )
2539 {
2540 PCB_LAYER_ID layer =
2541 FromProtoEnum<PCB_LAYER_ID>( static_cast<board::types::BoardLayer>( layerIdx ) );
2542
2543 if( enabled.Contains( layer ) )
2544 visible.set( layer );
2545 }
2546
2547 board()->SetVisibleLayers( visible );
2550 frame()->Refresh();
2551 return Empty();
2552}
2553
2554
2557{
2558 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetActiveLayer" ) )
2559 return tl::unexpected( *headless );
2560
2561 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2562
2563 if( !documentValidation )
2564 return tl::unexpected( documentValidation.error() );
2565
2566 BoardLayerResponse response;
2567 response.set_layer(
2569
2570 return response;
2571}
2572
2573
2576{
2577 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetActiveLayer" ) )
2578 return tl::unexpected( *headless );
2579
2580 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2581 return tl::unexpected( *busy );
2582
2583 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2584
2585 if( !documentValidation )
2586 return tl::unexpected( documentValidation.error() );
2587
2588 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( aCtx.Request.layer() );
2589
2590 if( !board()->GetEnabledLayers().Contains( layer ) )
2591 {
2592 ApiResponseStatus err;
2593 err.set_status( ApiStatusCode::AS_BAD_REQUEST );
2594 err.set_error_message( fmt::format( "Layer {} is not a valid layer for the given board",
2595 magic_enum::enum_name( layer ) ) );
2596 return tl::unexpected( err );
2597 }
2598
2599 frame()->SetActiveLayer( layer );
2600 return Empty();
2601}
2602
2603
2606{
2607 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetBoardEditorAppearanceSettings" ) )
2608 return tl::unexpected( *headless );
2609
2610 BoardEditorAppearanceSettings reply;
2611
2612 // TODO: might be nice to put all these things in one place and have it derive SERIALIZABLE
2613
2614 const PCB_DISPLAY_OPTIONS& displayOptions = frame()->GetDisplayOptions();
2615
2616 reply.set_inactive_layer_display( ToProtoEnum<HIGH_CONTRAST_MODE, InactiveLayerDisplayMode>(
2617 displayOptions.m_ContrastModeDisplay ) );
2618 reply.set_net_color_display(
2620
2621 reply.set_board_flip( frame()->GetCanvas()->GetView()->IsMirroredX()
2622 ? BoardFlipMode::BFM_FLIPPED_X
2623 : BoardFlipMode::BFM_NORMAL );
2624
2625 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
2626
2627 reply.set_ratsnest_display( ToProtoEnum<RATSNEST_MODE, RatsnestDisplayMode>(
2628 editorSettings->m_Display.m_RatsnestMode ) );
2629
2630 return reply;
2631}
2632
2633
2636{
2637 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetBoardEditorAppearanceSettings" ) )
2638 return tl::unexpected( *headless );
2639
2640 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2641 return tl::unexpected( *busy );
2642
2644 KIGFX::PCB_VIEW* view = frame()->GetCanvas()->GetView();
2645 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
2646 const BoardEditorAppearanceSettings& newSettings = aCtx.Request.settings();
2647
2648 options.m_ContrastModeDisplay =
2649 FromProtoEnum<HIGH_CONTRAST_MODE>( newSettings.inactive_layer_display() );
2650 options.m_NetColorMode =
2651 FromProtoEnum<NET_COLOR_MODE>( newSettings.net_color_display() );
2652
2653 bool flip = newSettings.board_flip() == BoardFlipMode::BFM_FLIPPED_X;
2654
2655 if( flip != view->IsMirroredX() )
2656 {
2657 view->SetMirror( !view->IsMirroredX(), view->IsMirroredY() );
2658 view->RecacheAllItems();
2659 }
2660
2661 editorSettings->m_Display.m_RatsnestMode =
2662 FromProtoEnum<RATSNEST_MODE>( newSettings.ratsnest_display() );
2663
2664 frame()->SetDisplayOptions( options );
2666 frame()->GetCanvas()->Refresh();
2667
2668 return Empty();
2669}
2670
2671
2674{
2675 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2676 return tl::unexpected( *busy );
2677
2678 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2679
2680 if( !documentValidation )
2681 return tl::unexpected( documentValidation.error() );
2682
2683 SEVERITY severity = FromProtoEnum<SEVERITY>( aCtx.Request.severity() );
2684 int layer = severity == RPT_SEVERITY_WARNING ? LAYER_DRC_WARNING : LAYER_DRC_ERROR;
2686
2687 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( code );
2688
2689 drcItem->SetErrorMessage( wxString::FromUTF8( aCtx.Request.message() ) );
2690
2691 RC_ITEM::KIIDS ids;
2692
2693 for( const auto& id : aCtx.Request.items() )
2694 ids.emplace_back( KIID( id.value() ) );
2695
2696 if( !ids.empty() )
2697 drcItem->SetItems( ids );
2698
2699 const auto& pos = aCtx.Request.position();
2700 VECTOR2I position( static_cast<int>( pos.x_nm() ), static_cast<int>( pos.y_nm() ) );
2701
2702 PCB_MARKER* marker = new PCB_MARKER( drcItem, position, layer );
2703
2704 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
2705 commit->Add( marker );
2706 commit->Push( wxS( "API injected DRC marker" ) );
2707
2708 InjectDrcErrorResponse response;
2709 response.mutable_marker()->set_value( marker->GetUUID().AsStdString() );
2710
2711 return response;
2712}
2713
2714
2715std::optional<ApiResponseStatus> ValidateUnitsInchMm( types::Units aUnits,
2716 const std::string& aCommandName )
2717{
2718 if( aUnits == types::Units::U_INCH || aUnits == types::Units::U_MM
2719 || aUnits == types::Units::U_UNKNOWN )
2720 {
2721 return std::nullopt;
2722 }
2723
2724 ApiResponseStatus e;
2725 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2726 e.set_error_message( fmt::format( "{} supports only inch and mm units", aCommandName ) );
2727 return e;
2728}
2729
2730
2731std::optional<ApiResponseStatus>
2732ValidatePaginationModeForSingleOrPerFile( kiapi::board::jobs::BoardJobPaginationMode aMode,
2733 const std::string& aCommandName )
2734{
2735 if( aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_UNKNOWN
2736 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_ALL_LAYERS_ONE_PAGE
2737 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_EACH_LAYER_OWN_FILE )
2738 {
2739 return std::nullopt;
2740 }
2741
2742 ApiResponseStatus e;
2743 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2744 e.set_error_message( fmt::format( "{} does not support EACH_LAYER_OWN_PAGE pagination mode",
2745 aCommandName ) );
2746 return e;
2747}
2748
2749
2750std::optional<ApiResponseStatus> ApplyBoardPlotSettings( const BoardPlotSettings& aSettings,
2751 JOB_EXPORT_PCB_PLOT& aJob )
2752{
2753 for( int layer : aSettings.layers() )
2754 {
2756 static_cast<board::types::BoardLayer>( layer ) );
2757
2758 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2759 {
2760 ApiResponseStatus e;
2761 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2762 e.set_error_message( "Board plot settings contain an invalid layer" );
2763 return e;
2764 }
2765
2766 aJob.m_plotLayerSequence.push_back( layerId );
2767 }
2768
2769 for( int layer : aSettings.common_layers() )
2770 {
2772 static_cast<board::types::BoardLayer>( layer ) );
2773
2774 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2775 {
2776 ApiResponseStatus e;
2777 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2778 e.set_error_message( "Board plot settings contain an invalid common layer" );
2779 return e;
2780 }
2781
2782 aJob.m_plotOnAllLayersSequence.push_back( layerId );
2783 }
2784
2785 aJob.m_colorTheme = wxString::FromUTF8( aSettings.color_theme() );
2786 aJob.m_drawingSheet = wxString::FromUTF8( aSettings.drawing_sheet() );
2787 aJob.m_variant = wxString::FromUTF8( aSettings.variant() );
2788
2789 aJob.m_mirror = aSettings.mirror();
2790 aJob.m_blackAndWhite = aSettings.black_and_white();
2791 aJob.m_negative = aSettings.negative();
2792 aJob.m_scale = aSettings.scale();
2793
2794 aJob.m_sketchPadsOnFabLayers = aSettings.sketch_pads_on_fab_layers();
2795 aJob.m_hideDNPFPsOnFabLayers = aSettings.hide_dnp_footprints_on_fab_layers();
2796 aJob.m_sketchDNPFPsOnFabLayers = aSettings.sketch_dnp_footprints_on_fab_layers();
2797 aJob.m_crossoutDNPFPsOnFabLayers = aSettings.crossout_dnp_footprints_on_fab_layers();
2798
2799 aJob.m_plotFootprintValues = aSettings.plot_footprint_values();
2800 aJob.m_plotRefDes = aSettings.plot_reference_designators();
2801 aJob.m_plotDrawingSheet = aSettings.plot_drawing_sheet();
2802 aJob.m_subtractSolderMaskFromSilk = aSettings.subtract_solder_mask_from_silk();
2803 aJob.m_plotPadNumbers = aSettings.plot_pad_numbers();
2804
2805 aJob.m_drillShapeOption = FromProtoEnum<DRILL_MARKS>( aSettings.drill_marks() );
2806
2807 aJob.m_useDrillOrigin = aSettings.use_drill_origin();
2808 aJob.m_checkZonesBeforePlot = aSettings.check_zones_before_plot();
2809
2810 return std::nullopt;
2811}
2812
2813
2815{
2816 types::RunJobResponse response;
2817 WX_STRING_REPORTER reporter;
2818
2819 if( !aContext || !aContext->GetKiway() )
2820 {
2821 response.set_status( types::JobStatus::JS_ERROR );
2822 response.set_message( "Internal error" );
2823 return response;
2824 wxCHECK_MSG( false, response, "context missing valid kiway in ExecuteBoardJob?" );
2825 }
2826
2827 int exitCode = aContext->GetKiway()->ProcessJob( KIWAY::FACE_PCB, &aJob, &reporter );
2828
2829 for( const JOB_OUTPUT& output : aJob.GetOutputs() )
2830 response.add_output_path( output.m_outputPath.ToUTF8() );
2831
2832 if( exitCode == 0 )
2833 {
2834 response.set_status( types::JobStatus::JS_SUCCESS );
2835 return response;
2836 }
2837
2838 response.set_status( types::JobStatus::JS_ERROR );
2839 response.set_message( fmt::format( "Board export job '{}' failed with exit code {}: {}",
2840 aJob.GetType(), exitCode,
2841 reporter.GetMessages().ToStdString() ) );
2842 return response;
2843}
2844
2845
2848{
2849 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2850 return tl::unexpected( *busy );
2851
2852 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2853
2854 if( !documentValidation )
2855 return tl::unexpected( documentValidation.error() );
2856
2859 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2860
2862
2863 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2864 job.m_3dparams.m_NetFilter = wxString::FromUTF8( aCtx.Request.net_filter() );
2865 job.m_3dparams.m_ComponentFilter = wxString::FromUTF8( aCtx.Request.component_filter() );
2866
2867 job.m_hasUserOrigin = aCtx.Request.has_user_origin();
2868 job.m_3dparams.m_Origin = VECTOR2D( aCtx.Request.origin().x_nm(), aCtx.Request.origin().y_nm() );
2869
2870 job.m_3dparams.m_Overwrite = aCtx.Request.overwrite();
2871 job.m_3dparams.m_UseGridOrigin = aCtx.Request.use_grid_origin();
2872 job.m_3dparams.m_UseDrillOrigin = aCtx.Request.use_drill_origin();
2873 job.m_3dparams.m_UseDefinedOrigin = aCtx.Request.use_defined_origin() || aCtx.Request.has_user_origin();
2874 job.m_3dparams.m_UsePcbCenterOrigin = aCtx.Request.use_pcb_center_origin();
2875
2876 job.m_3dparams.m_IncludeUnspecified = aCtx.Request.include_unspecified();
2877 job.m_3dparams.m_IncludeDNP = aCtx.Request.include_dnp();
2878 job.m_3dparams.m_SubstModels = aCtx.Request.substitute_models();
2879
2880 job.m_3dparams.m_BoardOutlinesChainingEpsilon = aCtx.Request.board_outlines_chaining_epsilon();
2881 job.m_3dparams.m_BoardOnly = aCtx.Request.board_only();
2882 job.m_3dparams.m_CutViasInBody = aCtx.Request.cut_vias_in_body();
2883 job.m_3dparams.m_ExportBoardBody = aCtx.Request.export_board_body();
2884 job.m_3dparams.m_ExportComponents = aCtx.Request.export_components();
2885 job.m_3dparams.m_ExportTracksVias = aCtx.Request.export_tracks_and_vias();
2886 job.m_3dparams.m_ExportPads = aCtx.Request.export_pads();
2887 job.m_3dparams.m_ExportZones = aCtx.Request.export_zones();
2888 job.m_3dparams.m_ExportInnerCopper = aCtx.Request.export_inner_copper();
2889 job.m_3dparams.m_ExportSilkscreen = aCtx.Request.export_silkscreen();
2890 job.m_3dparams.m_ExportSoldermask = aCtx.Request.export_soldermask();
2891 job.m_3dparams.m_FuseShapes = aCtx.Request.fuse_shapes();
2892 job.m_3dparams.m_FillAllVias = aCtx.Request.fill_all_vias();
2893 job.m_3dparams.m_OptimizeStep = aCtx.Request.optimize_step();
2894 job.m_3dparams.m_ExtraPadThickness = aCtx.Request.extra_pad_thickness();
2895
2897
2898 job.m_vrmlModelDir = wxString::FromUTF8( aCtx.Request.vrml_model_dir() );
2899 job.m_vrmlRelativePaths = aCtx.Request.vrml_relative_paths();
2900
2901 return ExecuteBoardJob( context(), job );
2902}
2903
2904
2907{
2908 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2909 return tl::unexpected( *busy );
2910
2911 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2912
2913 if( !documentValidation )
2914 return tl::unexpected( documentValidation.error() );
2915
2916 JOB_PCB_RENDER job;
2918 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2919
2922 job.m_bgStyle = FromProtoEnum<JOB_PCB_RENDER::BG_STYLE>( aCtx.Request.background_style() );
2923
2924 job.m_width = aCtx.Request.width();
2925 job.m_height = aCtx.Request.height();
2926 job.m_appearancePreset = aCtx.Request.appearance_preset();
2927 job.m_useBoardStackupColors = aCtx.Request.use_board_stackup_colors();
2928
2930
2931 job.m_zoom = aCtx.Request.zoom();
2932 job.m_perspective = aCtx.Request.perspective();
2933
2934 job.m_rotation = UnpackVector3D( aCtx.Request.rotation() );
2935 job.m_pan = UnpackVector3D( aCtx.Request.pan() );
2936 job.m_pivot = UnpackVector3D( aCtx.Request.pivot() );
2937
2938 job.m_proceduralTextures = aCtx.Request.procedural_textures();
2939 job.m_floor = aCtx.Request.floor();
2940 job.m_antiAlias = aCtx.Request.anti_alias();
2941 job.m_postProcess = aCtx.Request.post_process();
2942
2943 job.m_lightTopIntensity = UnpackVector3D( aCtx.Request.light_top_intensity() );
2944 job.m_lightBottomIntensity = UnpackVector3D( aCtx.Request.light_bottom_intensity() );
2945 job.m_lightCameraIntensity = UnpackVector3D( aCtx.Request.light_camera_intensity() );
2946 job.m_lightSideIntensity = UnpackVector3D( aCtx.Request.light_side_intensity() );
2947 job.m_lightSideElevation = aCtx.Request.light_side_elevation();
2948
2949 return ExecuteBoardJob( context(), job );
2950}
2951
2952
2955{
2956 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2957 return tl::unexpected( *busy );
2958
2959 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2960
2961 if( !documentValidation )
2962 return tl::unexpected( documentValidation.error() );
2963
2966 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2967
2968 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2969 return tl::unexpected( *err );
2970
2971 job.m_fitPageToBoard = aCtx.Request.fit_page_to_board();
2972 job.m_precision = aCtx.Request.precision();
2973
2974 if( std::optional<ApiResponseStatus> paginationError =
2976 "RunBoardJobExportSvg" ) )
2977 {
2978 return tl::unexpected( *paginationError );
2979 }
2980
2982
2983 return ExecuteBoardJob( context(), job );
2984}
2985
2986
2989{
2990 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2991 return tl::unexpected( *busy );
2992
2993 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2994
2995 if( !documentValidation )
2996 return tl::unexpected( documentValidation.error() );
2997
3000 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3001
3002 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
3003 return tl::unexpected( *err );
3004
3005 job.m_plotGraphicItemsUsingContours = aCtx.Request.plot_graphic_items_using_contours();
3006 job.m_polygonMode = aCtx.Request.polygon_mode();
3007
3008 if( std::optional<ApiResponseStatus> unitError =
3009 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDxf" ) )
3010 {
3011 return tl::unexpected( *unitError );
3012 }
3013
3015
3016 if( std::optional<ApiResponseStatus> paginationError =
3018 "RunBoardJobExportDxf" ) )
3019 {
3020 return tl::unexpected( *paginationError );
3021 }
3022
3024
3025 return ExecuteBoardJob( context(), job );
3026}
3027
3028
3031{
3032 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3033 return tl::unexpected( *busy );
3034
3035 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3036
3037 if( !documentValidation )
3038 return tl::unexpected( documentValidation.error() );
3039
3042 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3043
3044 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
3045 return tl::unexpected( *err );
3046
3047 job.m_pdfFrontFPPropertyPopups = aCtx.Request.front_footprint_property_popups();
3048 job.m_pdfBackFPPropertyPopups = aCtx.Request.back_footprint_property_popups();
3049 job.m_pdfMetadata = aCtx.Request.include_metadata();
3050 job.m_pdfSingle = aCtx.Request.single_document();
3051 job.m_pdfBackgroundColor = wxString::FromUTF8( aCtx.Request.background_color() );
3052
3054
3055 return ExecuteBoardJob( context(), job );
3056}
3057
3058
3061{
3062 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3063 return tl::unexpected( *busy );
3064
3065 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3066
3067 if( !documentValidation )
3068 return tl::unexpected( documentValidation.error() );
3069
3072 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3073
3074 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
3075 return tl::unexpected( *err );
3076
3077 if( std::optional<ApiResponseStatus> paginationError =
3079 "RunBoardJobExportPs" ) )
3080 {
3081 return tl::unexpected( *paginationError );
3082 }
3083
3085
3086 job.m_trackWidthCorrection = aCtx.Request.track_width_correction();
3087 job.m_XScaleAdjust = aCtx.Request.x_scale_adjust();
3088 job.m_YScaleAdjust = aCtx.Request.y_scale_adjust();
3089 job.m_forceA4 = aCtx.Request.force_a4();
3090 job.m_useGlobalSettings = aCtx.Request.use_global_settings();
3091
3092 return ExecuteBoardJob( context(), job );
3093}
3094
3095
3098{
3099 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3100 return tl::unexpected( *busy );
3101
3102 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3103
3104 if( !documentValidation )
3105 return tl::unexpected( documentValidation.error() );
3106
3107 if( aCtx.Request.layers().empty() )
3108 {
3109 ApiResponseStatus e;
3110 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3111 e.set_error_message( "RunBoardJobExportGerbers requires at least one layer" );
3112 return tl::unexpected( e );
3113 }
3114
3117 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3118
3119 for( int layer : aCtx.Request.layers() )
3120 {
3121 PCB_LAYER_ID layerId =
3123 static_cast<board::types::BoardLayer>( layer ) );
3124
3125 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
3126 {
3127 ApiResponseStatus e;
3128 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3129 e.set_error_message( "RunBoardJobExportGerbers contains an invalid layer" );
3130 return tl::unexpected( e );
3131 }
3132
3133 job.m_plotLayerSequence.push_back( layerId );
3134 }
3135
3136 return ExecuteBoardJob( context(), job );
3137}
3138
3139
3142{
3143 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3144 return tl::unexpected( *busy );
3145
3146 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3147
3148 if( !documentValidation )
3149 return tl::unexpected( documentValidation.error() );
3150
3153 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3154
3156
3157 if( std::optional<ApiResponseStatus> unitError =
3158 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDrill" ) )
3159 {
3160 return tl::unexpected( *unitError );
3161 }
3162
3166
3167 if( aCtx.Request.has_excellon() )
3168 {
3169 const ExcellonFormatOptions& excellonOptions = aCtx.Request.excellon();
3170
3171 if( excellonOptions.has_mirror_y() )
3172 job.m_excellonMirrorY = excellonOptions.mirror_y();
3173
3174 if( excellonOptions.has_minimal_header() )
3175 job.m_excellonMinimalHeader = excellonOptions.minimal_header();
3176
3177 if( excellonOptions.has_combine_pth_npth() )
3178 job.m_excellonCombinePTHNPTH = excellonOptions.combine_pth_npth();
3179
3180 if( excellonOptions.has_route_oval_holes() )
3181 job.m_excellonOvalDrillRoute = excellonOptions.route_oval_holes();
3182 }
3183
3184 if( aCtx.Request.map_format() != DrillMapFormat::DMF_UNKNOWN )
3185 {
3186 job.m_generateMap = true;
3188 }
3189
3190 job.m_gerberPrecision = aCtx.Request.gerber_precision() == DrillGerberPrecision::DGP_4_5 ? 5 : 6;
3191
3192 if( aCtx.Request.has_gerber_generate_tenting() )
3193 job.m_generateTenting = aCtx.Request.gerber_generate_tenting();
3194
3195 if( aCtx.Request.report_format() != DrillReportFormat::DRF_UNKNOWN )
3196 {
3197 job.m_generateReport = true;
3198
3199 if( aCtx.Request.has_report_filename() )
3200 job.m_reportPath = wxString::FromUTF8( aCtx.Request.report_filename() );
3201 }
3202
3203 return ExecuteBoardJob( context(), job );
3204}
3205
3206
3209{
3210 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3211 return tl::unexpected( *busy );
3212
3213 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3214
3215 if( !documentValidation )
3216 return tl::unexpected( documentValidation.error() );
3217
3220 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3221
3222 if( aCtx.Request.has_use_drill_place_file_origin() )
3223 job.m_useDrillPlaceFileOrigin = aCtx.Request.use_drill_place_file_origin();
3224
3225 job.m_smdOnly = aCtx.Request.smd_only();
3226 job.m_excludeFootprintsWithTh = aCtx.Request.exclude_footprints_with_th();
3227 job.m_excludeDNP = aCtx.Request.exclude_dnp();
3228 job.m_excludeBOM = aCtx.Request.exclude_from_bom();
3229 job.m_negateBottomX = aCtx.Request.negate_bottom_x();
3230 job.m_singleFile = aCtx.Request.single_file();
3231 job.m_nakedFilename = aCtx.Request.naked_filename();
3232 if( aCtx.Request.has_include_board_edge_for_gerber() )
3233 job.m_gerberBoardEdge = aCtx.Request.include_board_edge_for_gerber();
3234
3235 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
3236
3238
3239 if( std::optional<ApiResponseStatus> unitError =
3240 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportPosition" ) )
3241 {
3242 return tl::unexpected( *unitError );
3243 }
3244
3247
3248 return ExecuteBoardJob( context(), job );
3249}
3250
3251
3254{
3255 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3256 return tl::unexpected( *busy );
3257
3258 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3259
3260 if( !documentValidation )
3261 return tl::unexpected( documentValidation.error() );
3262
3265 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3266
3267 job.m_flipBottomPads = aCtx.Request.flip_bottom_pads();
3268 job.m_useIndividualShapes = aCtx.Request.use_individual_shapes();
3269 job.m_storeOriginCoords = aCtx.Request.store_origin_coords();
3270 job.m_useDrillOrigin = aCtx.Request.use_drill_origin();
3271 job.m_useUniquePins = aCtx.Request.use_unique_pins();
3272
3273 return ExecuteBoardJob( context(), job );
3274}
3275
3276
3279{
3280 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3281 return tl::unexpected( *busy );
3282
3283 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3284
3285 if( !documentValidation )
3286 return tl::unexpected( documentValidation.error() );
3287
3290 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3291
3292 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
3293 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
3294 if( aCtx.Request.has_precision() )
3295 job.m_precision = aCtx.Request.precision();
3296
3297 job.m_compress = aCtx.Request.compress();
3298 job.m_colInternalId = wxString::FromUTF8( aCtx.Request.internal_id_column() );
3299 job.m_colMfgPn = wxString::FromUTF8( aCtx.Request.manufacturer_part_number_column() );
3300 job.m_colMfg = wxString::FromUTF8( aCtx.Request.manufacturer_column() );
3301 job.m_colDistPn = wxString::FromUTF8( aCtx.Request.distributor_part_number_column() );
3302 job.m_colDist = wxString::FromUTF8( aCtx.Request.distributor_column() );
3303 job.m_bomRev = wxString::FromUTF8( aCtx.Request.bom_revision() );
3304
3305 if( std::optional<ApiResponseStatus> unitError =
3306 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportIpc2581" ) )
3307 {
3308 return tl::unexpected( *unitError );
3309 }
3310
3313
3314 return ExecuteBoardJob( context(), job );
3315}
3316
3317
3320{
3321 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3322 return tl::unexpected( *busy );
3323
3324 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3325
3326 if( !documentValidation )
3327 return tl::unexpected( documentValidation.error() );
3328
3331 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3332
3333 return ExecuteBoardJob( context(), job );
3334}
3335
3336
3339{
3340 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3341 return tl::unexpected( *busy );
3342
3343 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3344
3345 if( !documentValidation )
3346 return tl::unexpected( documentValidation.error() );
3347
3350 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3351
3352 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
3353 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
3354 if( aCtx.Request.has_precision() )
3355 job.m_precision = aCtx.Request.precision();
3356
3357 if( std::optional<ApiResponseStatus> unitError =
3358 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportODB" ) )
3359 {
3360 return tl::unexpected( *unitError );
3361 }
3362
3365
3366 return ExecuteBoardJob( context(), job );
3367}
3368
3369
3372{
3373 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3374 return tl::unexpected( *busy );
3375
3376 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3377
3378 if( !documentValidation )
3379 return tl::unexpected( documentValidation.error() );
3380
3383 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3384
3386
3387 if( std::optional<ApiResponseStatus> unitError =
3388 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportStats" ) )
3389 {
3390 return tl::unexpected( *unitError );
3391 }
3392
3394
3395 job.m_excludeFootprintsWithoutPads = aCtx.Request.exclude_footprints_without_pads();
3396 job.m_subtractHolesFromBoardArea = aCtx.Request.subtract_holes_from_board_area();
3397 job.m_subtractHolesFromCopperAreas = aCtx.Request.subtract_holes_from_copper_areas();
3398
3399 return ExecuteBoardJob( context(), job );
3400}
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:44
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition api_handler.h:45
std::optional< ApiResponseStatus > ValidatePaginationModeForSingleOrPerFile(kiapi::board::jobs::BoardJobPaginationMode aMode, const std::string &aCommandName)
std::optional< ApiResponseStatus > ApplyBoardPlotSettings(const BoardPlotSettings &aSettings, JOB_EXPORT_PCB_PLOT &aJob)
std::optional< ApiResponseStatus > ValidateUnitsInchMm(types::Units aUnits, const std::string &aCommandName)
HANDLER_RESULT< types::RunJobResponse > ExecuteBoardJob(BOARD_CONTEXT *aContext, JOB &aJob)
std::unique_ptr< EDA_ITEM > CreateItemForType(KICAD_T aType, EDA_ITEM *aContainer)
@ ERROR_INSIDE
BASE_SCREEN class implementation.
std::shared_ptr< BOARD_CONTEXT > CreatePcbFrameContext(PCB_EDIT_FRAME *aFrame)
@ LAYER_CLASS_COUNT
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION gridSetOrigin
Definition actions.h:195
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:232
HANDLER_RESULT< bool > validateDocument(const DocumentSpecifier &aDocument)
HANDLER_RESULT< types::PageSettings > handleSetPageSettings(const HANDLER_CONTEXT< commands::SetPageSettings > &aCtx)
HANDLER_RESULT< std::optional< KIID > > validateItemHeaderDocument(const kiapi::common::types::ItemHeader &aHeader)
If the header is valid, returns the item container.
HANDLER_RESULT< types::PageSettings > handleGetPageSettings(const HANDLER_CONTEXT< commands::GetPageSettings > &aCtx)
API_HANDLER_EDITOR(EDA_BASE_FRAME *aFrame=nullptr)
static std::vector< KICAD_T > parseRequestedItemTypes(const google::protobuf::RepeatedField< int > &aTypes)
COMMIT * getCurrentCommit(const std::string &aClientName)
virtual void pushCurrentCommit(const std::string &aClientName, const wxString &aMessage)
std::set< std::string > m_activeClients
virtual std::optional< ApiResponseStatus > checkForBusy()
Checks if the editor can accept commands.
EDA_BASE_FRAME * m_frame
HANDLER_RESULT< commands::SelectionResponse > handleAddToSelection(const HANDLER_CONTEXT< commands::AddToSelection > &aCtx)
HANDLER_RESULT< commands::CreateItemsResponse > handleParseAndCreateItemsFromString(const HANDLER_CONTEXT< commands::ParseAndCreateItemsFromString > &aCtx)
std::optional< TITLE_BLOCK * > getTitleBlock() override
HANDLER_RESULT< Empty > handleInteractiveMoveItems(const HANDLER_CONTEXT< InteractiveMoveItems > &aCtx)
HANDLER_RESULT< Empty > handleSetActiveLayer(const HANDLER_CONTEXT< SetActiveLayer > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPdf(const HANDLER_CONTEXT< RunBoardJobExportPdf > &aCtx)
HANDLER_RESULT< BoardDesignRulesResponse > handleSetBoardDesignRules(const HANDLER_CONTEXT< SetBoardDesignRules > &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
HANDLER_RESULT< commands::GetItemsResponse > handleGetConnectedItems(const HANDLER_CONTEXT< GetConnectedItems > &aCtx)
HANDLER_RESULT< types::Vector2 > handleGetBoardOrigin(const HANDLER_CONTEXT< GetBoardOrigin > &aCtx)
std::shared_ptr< BOARD_CONTEXT > m_context
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsByNetClass(const HANDLER_CONTEXT< GetItemsByNetClass > &aCtx)
bool setPageSettings(const PAGE_INFO &aPageInfo) override
std::unique_ptr< COMMIT > createCommit() override
Override this to create an appropriate COMMIT subclass for the frame in question.
BOARD_CONTEXT * context() const
HANDLER_RESULT< BoardStackupResponse > handleGetStackup(const HANDLER_CONTEXT< GetBoardStackup > &aCtx)
HANDLER_RESULT< commands::SelectionResponse > handleGetSelection(const HANDLER_CONTEXT< commands::GetSelection > &aCtx)
HANDLER_RESULT< NetClassForNetsResponse > handleGetNetClassForNets(const HANDLER_CONTEXT< GetNetClassForNets > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportIpcD356(const HANDLER_CONTEXT< RunBoardJobExportIpcD356 > &aCtx)
HANDLER_RESULT< BoardDesignRulesResponse > handleGetBoardDesignRules(const HANDLER_CONTEXT< GetBoardDesignRules > &aCtx)
std::optional< EDA_ITEM * > getItemFromDocument(const DocumentSpecifier &aDocument, const KIID &aId) override
HANDLER_RESULT< commands::ExpandTextVariablesResponse > handleExpandTextVariables(const HANDLER_CONTEXT< commands::ExpandTextVariables > &aCtx)
std::optional< ApiResponseStatus > checkForHeadless(const std::string &aCommandName) const
HANDLER_RESULT< Empty > handleSetVisibleLayers(const HANDLER_CONTEXT< SetVisibleLayers > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportGerbers(const HANDLER_CONTEXT< RunBoardJobExportGerbers > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportODB(const HANDLER_CONTEXT< RunBoardJobExportODB > &aCtx)
HANDLER_RESULT< Empty > handleSaveCopyOfDocument(const HANDLER_CONTEXT< commands::SaveCopyOfDocument > &aCtx)
HANDLER_RESULT< GraphicsDefaultsResponse > handleGetGraphicsDefaults(const HANDLER_CONTEXT< GetGraphicsDefaults > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExport3D(const HANDLER_CONTEXT< RunBoardJobExport3D > &aCtx)
TOOL_MANAGER * toolManager() const
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsById(const HANDLER_CONTEXT< commands::GetItemsById > &aCtx)
void setDrawingSheetFileName(const wxString &aFileName) override
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsByNet(const HANDLER_CONTEXT< GetItemsByNet > &aCtx)
std::optional< PAGE_INFO > getPageSettings() override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportIpc2581(const HANDLER_CONTEXT< RunBoardJobExportIpc2581 > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPosition(const HANDLER_CONTEXT< RunBoardJobExportPosition > &aCtx)
BOARD * board() const
HANDLER_RESULT< Empty > handleSetBoardOrigin(const HANDLER_CONTEXT< SetBoardOrigin > &aCtx)
HANDLER_RESULT< BoardLayerNameResponse > handleGetBoardLayerName(const HANDLER_CONTEXT< GetBoardLayerName > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportStats(const HANDLER_CONTEXT< RunBoardJobExportStats > &aCtx)
HANDLER_RESULT< Empty > handleClearSelection(const HANDLER_CONTEXT< commands::ClearSelection > &aCtx)
HANDLER_RESULT< commands::RunActionResponse > handleRunAction(const HANDLER_CONTEXT< commands::RunAction > &aCtx)
void onModified() override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPs(const HANDLER_CONTEXT< RunBoardJobExportPs > &aCtx)
HANDLER_RESULT< CustomRulesResponse > handleGetCustomDesignRules(const HANDLER_CONTEXT< GetCustomDesignRules > &aCtx)
HANDLER_RESULT< BoardLayers > handleGetVisibleLayers(const HANDLER_CONTEXT< GetVisibleLayers > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportSvg(const HANDLER_CONTEXT< RunBoardJobExportSvg > &aCtx)
HANDLER_RESULT< PadstackPresenceResponse > handleCheckPadstackPresenceOnLayers(const HANDLER_CONTEXT< CheckPadstackPresenceOnLayers > &aCtx)
HANDLER_RESULT< commands::SelectionResponse > handleRemoveFromSelection(const HANDLER_CONTEXT< commands::RemoveFromSelection > &aCtx)
HANDLER_RESULT< commands::GetOpenDocumentsResponse > handleGetOpenDocuments(const HANDLER_CONTEXT< commands::GetOpenDocuments > &aCtx)
HANDLER_RESULT< BoardEditorAppearanceSettings > handleGetBoardEditorAppearanceSettings(const HANDLER_CONTEXT< GetBoardEditorAppearanceSettings > &aCtx)
HANDLER_RESULT< NetsResponse > handleGetNets(const HANDLER_CONTEXT< GetNets > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportDxf(const HANDLER_CONTEXT< RunBoardJobExportDxf > &aCtx)
HANDLER_RESULT< commands::SavedDocumentResponse > handleSaveDocumentToString(const HANDLER_CONTEXT< commands::SaveDocumentToString > &aCtx)
HANDLER_RESULT< commands::GetBoundingBoxResponse > handleGetBoundingBox(const HANDLER_CONTEXT< commands::GetBoundingBox > &aCtx)
HANDLER_RESULT< Empty > handleSaveDocument(const HANDLER_CONTEXT< commands::SaveDocument > &aCtx)
void deleteItemsInternal(std::map< KIID, ItemDeletionStatus > &aItemsToDelete, const std::string &aClientName) override
PROJECT & project() const
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportDrill(const HANDLER_CONTEXT< RunBoardJobExportDrill > &aCtx)
HANDLER_RESULT< BoardEnabledLayersResponse > handleGetBoardEnabledLayers(const HANDLER_CONTEXT< GetBoardEnabledLayers > &aCtx)
HANDLER_RESULT< commands::GetItemsResponse > handleGetItems(const HANDLER_CONTEXT< commands::GetItems > &aCtx)
HANDLER_RESULT< PadShapeAsPolygonResponse > handleGetPadShapeAsPolygon(const HANDLER_CONTEXT< GetPadShapeAsPolygon > &aCtx)
HANDLER_RESULT< InjectDrcErrorResponse > handleInjectDrcError(const HANDLER_CONTEXT< InjectDrcError > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportRender(const HANDLER_CONTEXT< RunBoardJobExportRender > &aCtx)
PCB_EDIT_FRAME * frame() const
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportGencad(const HANDLER_CONTEXT< RunBoardJobExportGencad > &aCtx)
HANDLER_RESULT< BoardEnabledLayersResponse > handleSetBoardEnabledLayers(const HANDLER_CONTEXT< SetBoardEnabledLayers > &aCtx)
HANDLER_RESULT< Empty > handleSetBoardEditorAppearanceSettings(const HANDLER_CONTEXT< SetBoardEditorAppearanceSettings > &aCtx)
tl::expected< bool, ApiResponseStatus > validateDocumentInternal(const DocumentSpecifier &aDocument) const override
HANDLER_RESULT< Empty > handleRefillZones(const HANDLER_CONTEXT< RefillZones > &aCtx)
HANDLER_RESULT< types::ItemRequestStatus > handleCreateUpdateItemsInternal(bool aCreate, const std::string &aClientName, const types::ItemHeader &aHeader, const google::protobuf::RepeatedPtrField< google::protobuf::Any > &aItems, std::function< void(commands::ItemStatus, google::protobuf::Any)> aItemHandler) override
HANDLER_RESULT< BoardLayerResponse > handleGetActiveLayer(const HANDLER_CONTEXT< GetActiveLayer > &aCtx)
HANDLER_RESULT< Empty > handleRevertDocument(const HANDLER_CONTEXT< commands::RevertDocument > &aCtx)
HANDLER_RESULT< commands::SavedSelectionResponse > handleSaveSelectionToString(const HANDLER_CONTEXT< commands::SaveSelectionToString > &aCtx)
void pushCurrentCommit(const std::string &aClientName, const wxString &aMessage) override
HANDLER_RESULT< CustomRulesResponse > handleSetCustomDesignRules(const HANDLER_CONTEXT< SetCustomDesignRules > &aCtx)
wxString getDrawingSheetFileName() override
void registerHandler(HANDLER_RESULT< ResponseType >(HandlerType::*aHandler)(const HANDLER_CONTEXT< RequestType > &))
Registers an API command handler for the given message types.
Definition api_handler.h:93
static wxString m_DrawingSheetFileName
the name of the drawing sheet file, or empty to use the default drawing sheet
Definition base_screen.h:85
void SetContentModified(bool aModified=true)
Definition base_screen.h:59
BASE_SET & reset(size_t pos)
Definition base_set.h:143
BASE_SET & set(size_t pos)
Definition base_set.h:116
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
An interface for the frame surface that the API handlers need; to enable headless mode.
virtual bool SaveBoard()=0
virtual bool SavePcbCopy(const wxString &aFileName, bool aCreateProject, bool aHeadless)=0
virtual KIWAY * GetKiway() const =0
virtual BOARD * GetBoard() const =0
virtual PROJECT & Prj() const =0
virtual wxString GetCurrentFileName() const =0
Container for design settings for a BOARD object.
std::map< wxString, wxString > m_DrcExclusionComments
std::map< int, SEVERITY > m_DRCSeverities
bool m_TextUpright[LAYER_CLASS_COUNT]
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
std::set< wxString > m_DrcExclusions
const VECTOR2I & GetGridOrigin() const
TEARDROP_PARAMETERS_LIST m_TeardropParamsList
The parameters of teardrops for the different teardrop targets (via/pad, track end).
const VECTOR2I & GetAuxOrigin() const
int m_TextThickness[LAYER_CLASS_COUNT]
std::vector< int > m_TrackWidthList
std::vector< VALIDATION_ERROR > ValidateDesignRules(std::optional< EDA_UNITS > aUnits=std::nullopt) const
Validate design settings values and return per-field errors.
int m_LineThickness[LAYER_CLASS_COUNT]
VECTOR2I m_TextSize[LAYER_CLASS_COUNT]
bool m_TextItalic[LAYER_CLASS_COUNT]
std::vector< VIA_DIMENSION > m_ViasDimensionsList
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...
Definition board_item.h:84
virtual void CopyFrom(const BOARD_ITEM *aOther)
void ResetUuid()
Definition board_item.h:248
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:3135
void SetVisibleLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition board.cpp:1018
const PAGE_INFO & GetPageSettings() const
Definition board.h:807
void SetDesignSettings(const BOARD_DESIGN_SETTINGS &aSettings)
Definition board.cpp:1107
TITLE_BLOCK & GetTitleBlock()
Definition board.h:813
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition board.h:808
const wxString & GetFileName() const
Definition board.h:360
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1101
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:986
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1789
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:571
void SaveSelection(const PCB_SELECTION &selected, bool isFootprintEditor)
void SetWriter(std::function< void(const wxString &)> aWriter)
void SaveBoard(const wxString &aFileName, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
void SetBoard(BOARD *aBoard)
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:72
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:90
virtual void Push(const wxString &aMessage=wxT("A commit"), int aFlags=0)=0
Execute the changes.
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
void ToProto(kiapi::board::CustomRuleConstraint &aProto) const
Definition drc_rule.cpp:378
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:407
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
static wxString ExtractRuleComment(const wxString &aOriginalText)
Extract comment lines from a rule.
static wxString ExtractRuleText(const wxString &aContent, const wxString &aRuleName)
Extract the complete original text of a rule from file content.
static wxString FormatRuleFromProto(const kiapi::board::CustomRule &aRule, wxString *aErrorText=nullptr)
Definition drc_rule.cpp:84
void ReleaseFile()
Release the current file marked in use.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:27
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:120
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:118
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
virtual wxString GetFriendlyName() const
Definition eda_item.cpp:411
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
JOB_EXPORT_PCB_3D::FORMAT m_format
EXPORTER_STEP_PARAMS m_3dparams
Despite the name; also used for other formats.
ODB_COMPRESSION m_compressionMode
bool m_pdfSingle
This is a hack to deal with cli having the wrong behavior We will deprecate out the wrong behavior,...
GEN_MODE m_pdfGenMode
The background color specified in a hex string.
LSEQ m_plotOnAllLayersSequence
Used by SVG & PDF.
DRILL_MARKS m_drillShapeOption
Used by SVG/DXF/PDF/Gerbers.
bool m_mirror
Common Options.
LSEQ m_plotLayerSequence
Layers to include on all individual layer prints.
wxString m_variant
Variant name for variant-aware filtering.
VECTOR3D m_lightBottomIntensity
VECTOR3D m_lightTopIntensity
VECTOR3D m_lightCameraIntensity
VECTOR3D m_rotation
wxString m_filename
bool m_useBoardStackupColors
VECTOR3D m_lightSideIntensity
std::string m_appearancePreset
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:184
void SetConfiguredOutputPath(const wxString &aPath)
Sets the configured output path for the job, this path is always saved to file.
Definition job.cpp:157
const std::vector< JOB_OUTPUT > & GetOutputs()
Definition job.h:215
const std::string & GetType() const
Definition job.h:195
void SetMirror(bool aMirrorX, bool aMirrorY)
Control the mirroring of the VIEW.
Definition view.cpp:625
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:845
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition view.h:259
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:1538
bool IsMirroredY() const
Return true if view is flipped across the Y axis.
Definition view.h:267
Definition kiid.h:48
std::string AsStdString() const
Definition kiid.cpp:250
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:750
@ FACE_PCB
pcbnew DSO
Definition kiway.h:323
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static int NameToLayer(wxString &aName)
Return the layer number from a layer name.
Definition lset.cpp:117
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:42
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:284
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition netclass.cpp:136
Handle the data for a net.
Definition netinfo.h:50
NETCLASS * GetNetClass()
Definition netinfo.h:95
int GetNetCode() const
Definition netinfo.h:97
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:193
NETINFO_ITEM * GetNetItem(int aNetCode) const
Definition pad.h:55
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
DISPLAY_OPTIONS m_Display
static TOOL_ACTION zoneFillAll
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION drillSetOrigin
APPEARANCE_CONTROLS * GetAppearancePanel()
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
PCBNEW_SETTINGS * GetPcbNewSettings() const
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void SetDisplayOptions(const PCB_DISPLAY_OPTIONS &aOptions, bool aRefresh=true)
Update the current display options.
HIGH_CONTRAST_MODE m_ContrastModeDisplay
How inactive layers are displayed.
NET_COLOR_MODE m_NetColorMode
How to use color overrides on specific nets and netclasses.
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
void SyncLayersVisibility(const BOARD *aBoard)
Update "visibility" property of each layer of a given BOARD.
The main frame for Pcbnew.
void LoadDrawingSheet()
Load the drawing sheet file.
void SetActiveLayer(PCB_LAYER_ID aLayer) override
Change the currently active layer to aLayer and also update the APPEARANCE_CONTROLS.
void OnModify() override
Must be called after a board change to set the modified flag.
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
Load a KiCad board (.kicad_pcb) from aFileName.
void UpdateUserInterface()
Update the layer manager and other widgets from the board setup (layer and items visibility,...
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
const KIID GetUUID() const override
Definition pcb_marker.h:49
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition project.cpp:411
std::vector< KIID > KIIDS
Definition rc_item.h:86
int AddItemsToSel(const TOOL_EVENT &aEvent)
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
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
TEARDROP_PARAMETERS_LIST is a helper class to handle the list of TEARDROP_PARAMETERS needed to build ...
bool m_UseRoundShapesOnly
True to create teardrops for round shapes only.
bool m_TargetVias
True to create teardrops for vias.
bool m_TargetPTHPads
True to create teardrops for pads with holes.
bool m_TargetTrack2Track
True to create teardrops at the end of a track connected to the end of another track having a differe...
TEARDROP_PARAMETERS * GetParameters(TARGET_TD aTdType)
bool m_TargetSMDPads
True to create teardrops for pads SMD, edge connectors,.
TEARDROP_PARAMETARS is a helper class to handle parameters needed to build teardrops for a board thes...
double m_BestWidthRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxLen
max allowed length for teardrops in IU. <= 0 to disable
bool m_AllowUseTwoTracks
True to create teardrops using 2 track segments if the first in too small.
int m_TdMaxWidth
max allowed height for teardrops in IU. <= 0 to disable
double m_BestLengthRatio
The length of a teardrop as ratio between length and size of pad/via.
double m_WidthtoSizeFilterRatio
The ratio (H/D) between the via/pad size and the track width max value to create a teardrop 1....
bool m_TdOnPadsInZones
A filter to exclude pads inside zone fills.
bool m_Enabled
Flag to enable teardrops.
bool m_CurvedEdges
True if the teardrop should be curved.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Master controller class:
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
bool PostAPIAction(const TOOL_ACTION &aAction, COMMIT *aCommit)
A wrapper for reporting to a wxString object.
Definition reporter.h:193
const wxString & GetMessages() const
Definition reporter.cpp:105
A type-safe container of any type.
Definition ki_any.h:93
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:63
The common library.
PCB_DRC_CODE
Definition drc_item.h:38
@ DRCE_GENERIC_ERROR
Definition drc_item.h:91
@ DRCE_GENERIC_WARNING
Definition drc_item.h:90
#define _(s)
@ RECURSE
Definition eda_item.h:53
static const std::string DesignRulesFileExtension
static const std::string KiCadPcbFileExtension
#define KICTL_REVERT
reverting to a previously-saved (KiCad) file.
#define MAX_CU_LAYERS
Definition layer_ids.h:176
@ LAYER_DRC_WARNING
Layer for DRC markers with #SEVERITY_WARNING.
Definition layer_ids.h:301
@ LAYER_DRC_ERROR
Layer for DRC markers with #SEVERITY_ERROR.
Definition layer_ids.h:277
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ Edge_Cuts
Definition layer_ids.h:112
@ UNSELECTED_LAYER
Definition layer_ids.h:62
@ Margin
Definition layer_ids.h:113
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Rescue
Definition layer_ids.h:121
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition api_utils.cpp:35
KICOMMON_API VECTOR3D UnpackVector3D(const types::Vector3D &aInput)
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackBox2(types::Box2 &aOutput, const BOX2I &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackPolyLine(types::PolyLine &aOutput, const SHAPE_LINE_CHAIN &aSlc, const EDA_IU_SCALE &aScale)
STL namespace.
Class to handle a set of BOARD_ITEMs.
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
std::vector< EDA_ITEM * > EDA_ITEMS
std::string ClientName
Definition api_handler.h:51
RequestMessageType Request
Definition api_handler.h:52
@ TARGET_ROUND
@ TARGET_TRACK
nlohmann::json output
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:75
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:103
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:100
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:101
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:108
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:90
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:105
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:89
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:86
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:98
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:99
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:97
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:102
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686