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 <board_commit.h>
33#include <footprint.h>
34#include <kicad_clipboard.h>
35#include <netinfo.h>
36#include <pad.h>
37#include <pcb_edit_frame.h>
38#include <pcb_group.h>
39#include <pcb_reference_image.h>
40#include <pcb_shape.h>
41#include <pcb_text.h>
42#include <pcb_textbox.h>
43#include <pcb_track.h>
44#include <pcbnew_id.h>
45#include <pcb_marker.h>
46#include <kiway.h>
47#include <drc/drc_item.h>
62#include <jobs/job_pcb_render.h>
63#include <layer_ids.h>
64#include <project.h>
65#include <tool/tool_manager.h>
66#include <tools/pcb_actions.h>
68#include <zone.h>
69
70#include <api/common/types/base_types.pb.h>
73#include <drc/drc_rule_parser.h>
77#include <wx/ffile.h>
78#include <wx/filename.h>
79
80using namespace kiapi::common::commands;
81using types::CommandStatus;
82using types::DocumentType;
83using types::ItemRequestStatus;
84
85
90
91
92API_HANDLER_PCB::API_HANDLER_PCB( std::shared_ptr<BOARD_CONTEXT> aContext,
93 PCB_EDIT_FRAME* aFrame ) :
94 API_HANDLER_EDITOR( aFrame ),
95 m_context( std::move( aContext ) )
96{
97 wxCHECK( m_context, /* void */ );
98
105
108
114
139
148
165
194}
195
196
198{
199 return static_cast<PCB_EDIT_FRAME*>( m_frame );
200}
201
202
203std::optional<ApiResponseStatus> API_HANDLER_PCB::checkForHeadless( const std::string& aCommandName ) const
204{
205 if( frame() )
206 return std::nullopt;
207
208 ApiResponseStatus e;
209 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
210 e.set_error_message( fmt::format( "{} is not available in headless mode", aCommandName ) );
211 return e;
212}
213
214
216 const HANDLER_CONTEXT<RunAction>& aCtx )
217{
218 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RunAction" ) )
219 return tl::unexpected( *headless );
220
221 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
222 return tl::unexpected( *busy );
223
224 RunActionResponse response;
225
226 if( toolManager()->RunAction( aCtx.Request.action(), true ) )
227 response.set_status( RunActionStatus::RAS_OK );
228 else
229 response.set_status( RunActionStatus::RAS_INVALID );
230
231 return response;
232}
233
234
237{
238 if( aCtx.Request.type() != DocumentType::DOCTYPE_PCB )
239 {
240 ApiResponseStatus e;
241 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
242 e.set_status( ApiStatusCode::AS_UNHANDLED );
243 return tl::unexpected( e );
244 }
245
246 GetOpenDocumentsResponse response;
247 common::types::DocumentSpecifier doc;
248
249 wxFileName fn( context()->GetCurrentFileName() );
250
251 doc.set_type( DocumentType::DOCTYPE_PCB );
252 doc.set_board_filename( fn.GetFullName() );
253
254 doc.mutable_project()->set_name( project().GetProjectName().ToStdString() );
255 doc.mutable_project()->set_path( project().GetProjectDirectory().ToStdString() );
256
257 response.mutable_documents()->Add( std::move( doc ) );
258 return response;
259}
260
261
264{
265 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
266 return tl::unexpected( *busy );
267
268 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
269
270 if( !documentValidation )
271 return tl::unexpected( documentValidation.error() );
272
273 context()->SaveBoard();
274 return Empty();
275}
276
277
280{
281 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
282 return tl::unexpected( *busy );
283
284 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
285
286 if( !documentValidation )
287 return tl::unexpected( documentValidation.error() );
288
289 wxFileName boardPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.path() ) ) );
290
291 if( !boardPath.IsOk() || !boardPath.IsDirWritable() )
292 {
293 ApiResponseStatus e;
294 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
295 e.set_error_message( fmt::format( "save path '{}' could not be opened",
296 boardPath.GetFullPath().ToStdString() ) );
297 return tl::unexpected( e );
298 }
299
300 if( boardPath.FileExists()
301 && ( !boardPath.IsFileWritable() || !aCtx.Request.options().overwrite() ) )
302 {
303 ApiResponseStatus e;
304 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
305 e.set_error_message( fmt::format( "save path '{}' exists and cannot be overwritten",
306 boardPath.GetFullPath().ToStdString() ) );
307 return tl::unexpected( e );
308 }
309
310 if( boardPath.GetExt() != FILEEXT::KiCadPcbFileExtension )
311 {
312 ApiResponseStatus e;
313 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
314 e.set_error_message( fmt::format( "save path '{}' must have a kicad_pcb extension",
315 boardPath.GetFullPath().ToStdString() ) );
316 return tl::unexpected( e );
317 }
318
319 BOARD* board = this->board();
320
321 if( board->GetFileName().Matches( boardPath.GetFullPath() ) )
322 {
323 context()->SaveBoard();
324 return Empty();
325 }
326
327 bool includeProject = true;
328
329 if( aCtx.Request.has_options() )
330 includeProject = aCtx.Request.options().include_project();
331
332 context()->SavePcbCopy( boardPath.GetFullPath(), includeProject, /* aHeadless = */ true );
333
334 return Empty();
335}
336
337
340{
341 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RevertDocument" ) )
342 return tl::unexpected( *headless );
343
344 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
345 return tl::unexpected( *busy );
346
347 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
348
349 if( !documentValidation )
350 return tl::unexpected( documentValidation.error() );
351
352 wxFileName fn = project().AbsolutePath( board()->GetFileName() );
353
354 frame()->GetScreen()->SetContentModified( false );
355 frame()->ReleaseFile();
356 frame()->OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ), KICTL_REVERT );
357
358 return Empty();
359}
360
361
362void API_HANDLER_PCB::pushCurrentCommit( const std::string& aClientName, const wxString& aMessage )
363{
364 API_HANDLER_EDITOR::pushCurrentCommit( aClientName, aMessage );
365
366 if( frame() )
367 frame()->Refresh();
368}
369
370
371std::unique_ptr<COMMIT> API_HANDLER_PCB::createCommit()
372{
373 if( frame() )
374 return std::make_unique<BOARD_COMMIT>( frame() );
375
376 return std::make_unique<BOARD_COMMIT>( toolManager(), true, false );
377}
378
379
380std::optional<BOARD_ITEM*> API_HANDLER_PCB::getItemById( const KIID& aId ) const
381{
382 BOARD_ITEM* item = board()->ResolveItem( aId, true );
383
384 if( !item )
385 return std::nullopt;
386
387 return item;
388}
389
390
391bool API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
392{
393 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
394 return false;
395
396 wxFileName fn( context()->GetCurrentFileName() );
397 return 0 == aDocument.board_filename().compare( fn.GetFullName() );
398}
399
400
402 BOARD_ITEM_CONTAINER* aContainer )
403{
404 if( !aContainer )
405 {
406 ApiResponseStatus e;
407 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
408 e.set_error_message( "Tried to create an item in a null container" );
409 return tl::unexpected( e );
410 }
411
412 if( aType == PCB_PAD_T && !dynamic_cast<FOOTPRINT*>( aContainer ) )
413 {
414 ApiResponseStatus e;
415 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
416 e.set_error_message( fmt::format( "Tried to create a pad in {}, which is not a footprint",
417 aContainer->GetFriendlyName().ToStdString() ) );
418 return tl::unexpected( e );
419 }
420 else if( aType == PCB_FOOTPRINT_T && !dynamic_cast<BOARD*>( aContainer ) )
421 {
422 ApiResponseStatus e;
423 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
424 e.set_error_message( fmt::format( "Tried to create a footprint in {}, which is not a board",
425 aContainer->GetFriendlyName().ToStdString() ) );
426 return tl::unexpected( e );
427 }
428
429 std::unique_ptr<BOARD_ITEM> created = CreateItemForType( aType, aContainer );
430
431 if( !created )
432 {
433 ApiResponseStatus e;
434 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
435 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
436 magic_enum::enum_name( aType ) ) );
437 return tl::unexpected( e );
438 }
439
440 return created;
441}
442
443
445 const std::string& aClientName,
446 const types::ItemHeader &aHeader,
447 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
448 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
449{
450 ApiResponseStatus e;
451
452 auto containerResult = validateItemHeaderDocument( aHeader );
453
454 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
455 {
456 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
457 e.set_status( ApiStatusCode::AS_UNHANDLED );
458 return tl::unexpected( e );
459 }
460 else if( !containerResult )
461 {
462 e.CopyFrom( containerResult.error() );
463 return tl::unexpected( e );
464 }
465
466 BOARD* board = this->board();
467 BOARD_ITEM_CONTAINER* container = board;
468
469 if( containerResult->has_value() )
470 {
471 const KIID& containerId = **containerResult;
472 std::optional<BOARD_ITEM*> optItem = getItemById( containerId );
473
474 if( optItem )
475 {
476 container = dynamic_cast<BOARD_ITEM_CONTAINER*>( *optItem );
477
478 if( !container )
479 {
480 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
481 e.set_error_message( fmt::format(
482 "The requested container {} is not a valid board item container",
483 containerId.AsStdString() ) );
484 return tl::unexpected( e );
485 }
486 }
487 else
488 {
489 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
490 e.set_error_message( fmt::format(
491 "The requested container {} does not exist in this document",
492 containerId.AsStdString() ) );
493 return tl::unexpected( e );
494 }
495 }
496
497 BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aClientName ) );
498
499 for( const google::protobuf::Any& anyItem : aItems )
500 {
501 ItemStatus status;
502 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
503
504 if( !type )
505 {
506 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
507 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
508 anyItem.type_url() ) );
509 aItemHandler( status, anyItem );
510 continue;
511 }
512
513 if( type == PCB_DIMENSION_T )
514 {
515 board::types::Dimension dimension;
516 anyItem.UnpackTo( &dimension );
517
518 switch( dimension.dimension_style_case() )
519 {
520 case board::types::Dimension::kAligned: type = PCB_DIM_ALIGNED_T; break;
521 case board::types::Dimension::kOrthogonal: type = PCB_DIM_ORTHOGONAL_T; break;
522 case board::types::Dimension::kRadial: type = PCB_DIM_RADIAL_T; break;
523 case board::types::Dimension::kLeader: type = PCB_DIM_LEADER_T; break;
524 case board::types::Dimension::kCenter: type = PCB_DIM_CENTER_T; break;
525 case board::types::Dimension::DIMENSION_STYLE_NOT_SET: break;
526 }
527 }
528
530 createItemForType( *type, container );
531
532 if( !creationResult )
533 {
534 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
535 status.set_error_message( creationResult.error().error_message() );
536 aItemHandler( status, anyItem );
537 continue;
538 }
539
540 std::unique_ptr<BOARD_ITEM> item( std::move( *creationResult ) );
541
542 if( !item->Deserialize( anyItem ) )
543 {
544 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
545 e.set_error_message( fmt::format( "could not unpack {} from request",
546 item->GetClass().ToStdString() ) );
547 return tl::unexpected( e );
548 }
549
550 std::optional<BOARD_ITEM*> optItem = getItemById( item->m_Uuid );
551
552 if( aCreate && optItem )
553 {
554 status.set_code( ItemStatusCode::ISC_EXISTING );
555 status.set_error_message( fmt::format( "an item with UUID {} already exists",
556 item->m_Uuid.AsStdString() ) );
557 aItemHandler( status, anyItem );
558 continue;
559 }
560 else if( !aCreate && !optItem )
561 {
562 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
563 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
564 item->m_Uuid.AsStdString() ) );
565 aItemHandler( status, anyItem );
566 continue;
567 }
568
569 if( aCreate && !( board->GetEnabledLayers() & item->GetLayerSet() ).any() )
570 {
571 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
572 status.set_error_message(
573 "attempted to add item with no overlapping layers with the board" );
574 aItemHandler( status, anyItem );
575 continue;
576 }
577
578 status.set_code( ItemStatusCode::ISC_OK );
579 google::protobuf::Any newItem;
580
581 if( aCreate )
582 {
583 if( item->Type() == PCB_FOOTPRINT_T )
584 {
585 // Ensure children have unique identifiers; in case the API client created this new
586 // footprint by cloning an existing one and only changing the parent UUID.
587 item->RunOnChildren(
588 []( BOARD_ITEM* aChild )
589 {
590 aChild->ResetUuid();
591 },
592 RECURSE );
593 }
594
595 item->Serialize( newItem );
596 commit->Add( item.release() );
597 }
598 else
599 {
600 BOARD_ITEM* boardItem = *optItem;
601
602 // Footprints can't be modified by CopyFrom at the moment because the commit system
603 // doesn't currently know what to do with a footprint that has had its children
604 // replaced with other children; which results in things like the view not having its
605 // cached geometry for footprint children updated when you move a footprint around.
606 // And also, groups are special because they can contain any item type, so we
607 // can't use CopyFrom on them either.
608 if( boardItem->Type() == PCB_FOOTPRINT_T || boardItem->Type() == PCB_GROUP_T )
609 {
610 // Save group membership before removal, since Remove() severs the relationship
611 PCB_GROUP* parentGroup = dynamic_cast<PCB_GROUP*>( boardItem->GetParentGroup() );
612
613 commit->Remove( boardItem );
614 item->Serialize( newItem );
615
616 BOARD_ITEM* newBoardItem = item.release();
617 commit->Add( newBoardItem );
618
619 // Restore group membership for the newly added item
620 if( parentGroup )
621 parentGroup->AddItem( newBoardItem );
622 }
623 else
624 {
625 commit->Modify( boardItem );
626 boardItem->CopyFrom( item.get() );
627 boardItem->Serialize( newItem );
628 }
629 }
630
631 aItemHandler( status, newItem );
632 }
633
634 if( !m_activeClients.count( aClientName ) )
635 {
636 pushCurrentCommit( aClientName, aCreate ? _( "Created items via API" )
637 : _( "Modified items via API" ) );
638 }
639
640
641 return ItemRequestStatus::IRS_OK;
642}
643
644
646{
647 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
648 return tl::unexpected( *busy );
649
650 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
651 {
652 ApiResponseStatus e;
653 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
654 e.set_status( ApiStatusCode::AS_UNHANDLED );
655 return tl::unexpected( e );
656 }
657
658 GetItemsResponse response;
659
660 BOARD* board = this->board();
661 std::vector<BOARD_ITEM*> items;
662 std::set<KICAD_T> typesRequested, typesInserted;
663 bool handledAnything = false;
664
665 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
666 {
667 typesRequested.emplace( type );
668
669 if( typesInserted.count( type ) )
670 continue;
671
672 switch( type )
673 {
674 case PCB_TRACE_T:
675 case PCB_ARC_T:
676 case PCB_VIA_T:
677 handledAnything = true;
678 std::copy( board->Tracks().begin(), board->Tracks().end(),
679 std::back_inserter( items ) );
680 typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
681 break;
682
683 case PCB_PAD_T:
684 {
685 handledAnything = true;
686
687 for( FOOTPRINT* fp : board->Footprints() )
688 {
689 std::copy( fp->Pads().begin(), fp->Pads().end(),
690 std::back_inserter( items ) );
691 }
692
693 typesInserted.insert( PCB_PAD_T );
694 break;
695 }
696
697 case PCB_FOOTPRINT_T:
698 {
699 handledAnything = true;
700
701 std::copy( board->Footprints().begin(), board->Footprints().end(),
702 std::back_inserter( items ) );
703
704 typesInserted.insert( PCB_FOOTPRINT_T );
705 break;
706 }
707
708 case PCB_SHAPE_T:
709 case PCB_TEXT_T:
710 case PCB_TEXTBOX_T:
711 case PCB_BARCODE_T:
713 {
714 handledAnything = true;
715 bool inserted = false;
716
717 for( BOARD_ITEM* item : board->Drawings() )
718 {
719 if( item->Type() == type )
720 {
721 items.emplace_back( item );
722 inserted = true;
723 }
724 }
725
726 if( inserted )
727 typesInserted.insert( type );
728
729 break;
730 }
731
732 case PCB_DIMENSION_T:
733 {
734 handledAnything = true;
735 bool inserted = false;
736
737 for( BOARD_ITEM* item : board->Drawings() )
738 {
739 switch (item->Type()) {
741 case PCB_DIM_CENTER_T:
742 case PCB_DIM_RADIAL_T:
744 case PCB_DIM_LEADER_T:
745 items.emplace_back( item );
746 inserted = true;
747 break;
748 default:
749 break;
750 }
751 }
752 // we have to add the dimension subtypes to the requested to get them out
754
755 if( inserted )
757
758 break;
759 }
760
761 case PCB_ZONE_T:
762 {
763 handledAnything = true;
764
765 std::copy( board->Zones().begin(), board->Zones().end(),
766 std::back_inserter( items ) );
767
768 typesInserted.insert( PCB_ZONE_T );
769 break;
770 }
771
772 case PCB_GROUP_T:
773 {
774 handledAnything = true;
775
776 std::copy( board->Groups().begin(), board->Groups().end(),
777 std::back_inserter( items ) );
778
779 typesInserted.insert( PCB_GROUP_T );
780 break;
781 }
782 default:
783 break;
784 }
785 }
786
787 if( !handledAnything )
788 {
789 ApiResponseStatus e;
790 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
791 e.set_error_message( "none of the requested types are valid for a Board object" );
792 return tl::unexpected( e );
793 }
794
795 for( const BOARD_ITEM* item : items )
796 {
797 if( !typesRequested.count( item->Type() ) )
798 continue;
799
800 google::protobuf::Any itemBuf;
801 item->Serialize( itemBuf );
802 response.mutable_items()->Add( std::move( itemBuf ) );
803 }
804
805 response.set_status( ItemRequestStatus::IRS_OK );
806 return response;
807}
808
809
812{
813 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
814 return tl::unexpected( *busy );
815
816 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
817 {
818 ApiResponseStatus e;
819 e.set_status( ApiStatusCode::AS_UNHANDLED );
820 return tl::unexpected( e );
821 }
822
823 GetItemsResponse response;
824
825 std::vector<BOARD_ITEM*> items;
826
827 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
828 {
829 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
830 items.emplace_back( *item );
831 }
832
833 if( items.empty() )
834 {
835 ApiResponseStatus e;
836 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
837 e.set_error_message( "none of the requested IDs were found or valid" );
838 return tl::unexpected( e );
839 }
840
841 for( const BOARD_ITEM* item : items )
842 {
843 google::protobuf::Any itemBuf;
844 item->Serialize( itemBuf );
845 response.mutable_items()->Add( std::move( itemBuf ) );
846 }
847
848 response.set_status( ItemRequestStatus::IRS_OK );
849 return response;
850}
851
852void API_HANDLER_PCB::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
853 const std::string& aClientName )
854{
855 BOARD* board = this->board();
856 std::vector<BOARD_ITEM*> validatedItems;
857
858 for( std::pair<const KIID, ItemDeletionStatus> pair : aItemsToDelete )
859 {
860 if( BOARD_ITEM* item = board->ResolveItem( pair.first, true ) )
861 {
862 validatedItems.push_back( item );
863 aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK;
864 }
865
866 // Note: we don't currently support locking items from API modification, but here is where
867 // to add it in the future (and return IDS_IMMUTABLE)
868 }
869
870 COMMIT* commit = getCurrentCommit( aClientName );
871
872 for( BOARD_ITEM* item : validatedItems )
873 commit->Remove( item );
874
875 if( !m_activeClients.count( aClientName ) )
876 pushCurrentCommit( aClientName, _( "Deleted items via API" ) );
877}
878
879
880std::optional<EDA_ITEM*> API_HANDLER_PCB::getItemFromDocument( const DocumentSpecifier& aDocument,
881 const KIID& aId )
882{
883 if( !validateDocument( aDocument ) )
884 return std::nullopt;
885
886 return getItemById( aId );
887}
888
889
892{
893 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetSelection" ) )
894 return tl::unexpected( *headless );
895
896 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
897 {
898 ApiResponseStatus e;
899 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
900 e.set_status( ApiStatusCode::AS_UNHANDLED );
901 return tl::unexpected( e );
902 }
903
904 std::set<KICAD_T> filter;
905
906 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
907 filter.insert( type );
908
909 TOOL_MANAGER* mgr = toolManager();
910 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
911
912 SelectionResponse response;
913
914 for( EDA_ITEM* item : selectionTool->GetSelection() )
915 {
916 if( filter.empty() || filter.contains( item->Type() ) )
917 item->Serialize( *response.add_items() );
918 }
919
920 return response;
921}
922
923
926{
927 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "ClearSelection" ) )
928 return tl::unexpected( *headless );
929
930 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
931 return tl::unexpected( *busy );
932
933 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
934 {
935 ApiResponseStatus e;
936 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
937 e.set_status( ApiStatusCode::AS_UNHANDLED );
938 return tl::unexpected( e );
939 }
940
941 TOOL_MANAGER* mgr = toolManager();
943 frame()->Refresh();
944
945 return Empty();
946}
947
948
951{
952 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "AddToSelection" ) )
953 return tl::unexpected( *headless );
954
955 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
956 return tl::unexpected( *busy );
957
958 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
959 {
960 ApiResponseStatus e;
961 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
962 e.set_status( ApiStatusCode::AS_UNHANDLED );
963 return tl::unexpected( e );
964 }
965
966 TOOL_MANAGER* mgr = toolManager();
967 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
968
969 std::vector<EDA_ITEM*> toAdd;
970
971 for( const types::KIID& id : aCtx.Request.items() )
972 {
973 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
974 toAdd.emplace_back( *item );
975 }
976
977 selectionTool->AddItemsToSel( &toAdd );
978 frame()->Refresh();
979
980 SelectionResponse response;
981
982 for( EDA_ITEM* item : selectionTool->GetSelection() )
983 item->Serialize( *response.add_items() );
984
985 return response;
986}
987
988
991{
992 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RemoveFromSelection" ) )
993 return tl::unexpected( *headless );
994
995 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
996 return tl::unexpected( *busy );
997
998 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
999 {
1000 ApiResponseStatus e;
1001 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
1002 e.set_status( ApiStatusCode::AS_UNHANDLED );
1003 return tl::unexpected( e );
1004 }
1005
1006 TOOL_MANAGER* mgr = toolManager();
1007 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
1008
1009 std::vector<EDA_ITEM*> toRemove;
1010
1011 for( const types::KIID& id : aCtx.Request.items() )
1012 {
1013 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
1014 toRemove.emplace_back( *item );
1015 }
1016
1017 selectionTool->RemoveItemsFromSel( &toRemove );
1018 frame()->Refresh();
1019
1020 SelectionResponse response;
1021
1022 for( EDA_ITEM* item : selectionTool->GetSelection() )
1023 item->Serialize( *response.add_items() );
1024
1025 return response;
1026}
1027
1028
1031{
1032 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1033
1034 if( !documentValidation )
1035 return tl::unexpected( documentValidation.error() );
1036
1037 BoardStackupResponse response;
1038 google::protobuf::Any any;
1039
1041
1042 any.UnpackTo( response.mutable_stackup() );
1043
1044 // User-settable layer names are not stored in BOARD_STACKUP at the moment
1045 for( board::BoardStackupLayer& layer : *response.mutable_stackup()->mutable_layers() )
1046 {
1047 if( layer.type() == board::BoardStackupLayerType::BSLT_DIELECTRIC )
1048 continue;
1049
1050 PCB_LAYER_ID id = FromProtoEnum<PCB_LAYER_ID>( layer.layer() );
1051 wxCHECK2( id != UNDEFINED_LAYER, continue );
1052
1053 layer.set_user_name( board()->GetLayerName( id ) );
1054 }
1055
1056 return response;
1057}
1058
1059
1062{
1063 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1064
1065 if( !documentValidation )
1066 return tl::unexpected( documentValidation.error() );
1067
1068 BoardEnabledLayersResponse response;
1069
1070 BOARD* board = this->board();
1071 int copperLayerCount = board->GetCopperLayerCount();
1072
1073 response.set_copper_layer_count( copperLayerCount );
1074
1075 LSET enabled = board->GetEnabledLayers();
1076
1077 // The Rescue layer is an internal detail and should be hidden from the API
1078 enabled.reset( Rescue );
1079
1080 // Just in case this is out of sync; the API should always return the expected copper layers
1081 enabled |= LSET::AllCuMask( copperLayerCount );
1082
1083 board::PackLayerSet( *response.mutable_layers(), enabled );
1084
1085 return response;
1086}
1087
1088
1091{
1092 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1093
1094 if( !documentValidation )
1095 return tl::unexpected( documentValidation.error() );
1096
1097 if( aCtx.Request.copper_layer_count() % 2 != 0 )
1098 {
1099 ApiResponseStatus e;
1100 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1101 e.set_error_message( "copper_layer_count must be an even number" );
1102 return tl::unexpected( e );
1103 }
1104
1105 if( aCtx.Request.copper_layer_count() > MAX_CU_LAYERS )
1106 {
1107 ApiResponseStatus e;
1108 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1109 e.set_error_message( fmt::format( "copper_layer_count must be below %d", MAX_CU_LAYERS ) );
1110 return tl::unexpected( e );
1111 }
1112
1113 int copperLayerCount = static_cast<int>( aCtx.Request.copper_layer_count() );
1114 LSET enabled = board::UnpackLayerSet( aCtx.Request.layers() );
1115
1116 // Sanitize the input
1117 enabled |= LSET( { Edge_Cuts, Margin, F_CrtYd, B_CrtYd } );
1118 enabled &= ~LSET::AllCuMask();
1119 enabled |= LSET::AllCuMask( copperLayerCount );
1120
1121 BOARD* board = this->board();
1122
1123 LSET previousEnabled = board->GetEnabledLayers();
1124 LSET changedLayers = enabled ^ previousEnabled;
1125
1126 board->SetEnabledLayers( enabled );
1127 board->SetVisibleLayers( board->GetVisibleLayers() | changedLayers );
1128
1129 LSEQ removedLayers;
1130
1131 for( PCB_LAYER_ID layer_id : previousEnabled )
1132 {
1133 if( !enabled[layer_id] && board->HasItemsOnLayer( layer_id ) )
1134 removedLayers.push_back( layer_id );
1135 }
1136
1137 bool modified = false;
1138
1139 if( !removedLayers.empty() )
1140 {
1142
1143 for( PCB_LAYER_ID layer_id : removedLayers )
1144 modified |= board->RemoveAllItemsOnLayer( layer_id );
1145 }
1146
1147 if( enabled != previousEnabled )
1149
1150 if( modified )
1151 frame()->OnModify();
1152
1153 BoardEnabledLayersResponse response;
1154
1155 response.set_copper_layer_count( copperLayerCount );
1156 board::PackLayerSet( *response.mutable_layers(), enabled );
1157
1158 return response;
1159}
1160
1161
1164{
1165 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1166
1167 if( !documentValidation )
1168 return tl::unexpected( documentValidation.error() );
1169
1171 GraphicsDefaultsResponse response;
1172
1173 // TODO: This should change to be an enum class
1174 constexpr std::array<kiapi::board::BoardLayerClass, LAYER_CLASS_COUNT> classOrder = {
1175 kiapi::board::BLC_SILKSCREEN,
1176 kiapi::board::BLC_COPPER,
1177 kiapi::board::BLC_EDGES,
1178 kiapi::board::BLC_COURTYARD,
1179 kiapi::board::BLC_FABRICATION,
1180 kiapi::board::BLC_OTHER
1181 };
1182
1183 for( int i = 0; i < LAYER_CLASS_COUNT; ++i )
1184 {
1185 kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers();
1186
1187 l->set_layer( classOrder[i] );
1188 l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] );
1189
1190 kiapi::common::types::TextAttributes* text = l->mutable_text();
1191 text->mutable_size()->set_x_nm( bds.m_TextSize[i].x );
1192 text->mutable_size()->set_y_nm( bds.m_TextSize[i].y );
1193 text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] );
1194 text->set_italic( bds.m_TextItalic[i] );
1195 text->set_keep_upright( bds.m_TextUpright[i] );
1196 }
1197
1198 return response;
1199}
1200
1201
1204{
1205 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1206
1207 if( !documentValidation )
1208 return tl::unexpected( documentValidation.error() );
1209
1211 BoardDesignRulesResponse response;
1212 kiapi::board::BoardDesignRules* rules = response.mutable_rules();
1213
1214 kiapi::board::MinimumConstraints* constraints = rules->mutable_constraints();
1215
1216 constraints->mutable_min_clearance()->set_value_nm( bds.m_MinClearance );
1217 constraints->mutable_min_groove_width()->set_value_nm( bds.m_MinGrooveWidth );
1218 constraints->mutable_min_connection_width()->set_value_nm( bds.m_MinConn );
1219 constraints->mutable_min_track_width()->set_value_nm( bds.m_TrackMinWidth );
1220 constraints->mutable_min_via_annular_width()->set_value_nm( bds.m_ViasMinAnnularWidth );
1221 constraints->mutable_min_via_size()->set_value_nm( bds.m_ViasMinSize );
1222 constraints->mutable_min_through_drill()->set_value_nm( bds.m_MinThroughDrill );
1223 constraints->mutable_min_microvia_size()->set_value_nm( bds.m_MicroViasMinSize );
1224 constraints->mutable_min_microvia_drill()->set_value_nm( bds.m_MicroViasMinDrill );
1225 constraints->mutable_copper_edge_clearance()->set_value_nm( bds.m_CopperEdgeClearance );
1226 constraints->mutable_hole_clearance()->set_value_nm( bds.m_HoleClearance );
1227 constraints->mutable_hole_to_hole_min()->set_value_nm( bds.m_HoleToHoleMin );
1228 constraints->mutable_silk_clearance()->set_value_nm( bds.m_SilkClearance );
1229 constraints->set_min_resolved_spokes( bds.m_MinResolvedSpokes );
1230 constraints->mutable_min_silk_text_height()->set_value_nm( bds.m_MinSilkTextHeight );
1231 constraints->mutable_min_silk_text_thickness()->set_value_nm( bds.m_MinSilkTextThickness );
1232
1233 kiapi::board::PredefinedSizes* sizes = rules->mutable_predefined_sizes();
1234
1235 for( size_t ii = 1; ii < bds.m_TrackWidthList.size(); ++ii )
1236 sizes->add_tracks()->mutable_width()->set_value_nm( bds.m_TrackWidthList[ii] );
1237
1238 for( size_t ii = 1; ii < bds.m_ViasDimensionsList.size(); ++ii )
1239 {
1240 kiapi::board::PresetViaDimension* via = sizes->add_vias();
1241 via->mutable_diameter()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Diameter );
1242 via->mutable_drill()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Drill );
1243 }
1244
1245 for( size_t ii = 1; ii < bds.m_DiffPairDimensionsList.size(); ++ii )
1246 {
1247 kiapi::board::PresetDiffPairDimension* pair = sizes->add_diff_pairs();
1248 pair->mutable_width()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Width );
1249 pair->mutable_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Gap );
1250 pair->mutable_via_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_ViaGap );
1251 }
1252
1253 kiapi::board::SolderMaskPasteDefaults* maskPaste = rules->mutable_solder_mask_paste();
1254
1255 maskPaste->mutable_mask_expansion()->set_value_nm( bds.m_SolderMaskExpansion );
1256 maskPaste->mutable_mask_min_width()->set_value_nm( bds.m_SolderMaskMinWidth );
1257 maskPaste->mutable_mask_to_copper_clearance()->set_value_nm( bds.m_SolderMaskToCopperClearance );
1258 maskPaste->mutable_paste_margin()->set_value_nm( bds.m_SolderPasteMargin );
1259 maskPaste->set_paste_margin_ratio( bds.m_SolderPasteMarginRatio );
1260 maskPaste->set_allow_soldermask_bridges_in_footprints( bds.m_AllowSoldermaskBridgesInFPs );
1261
1262 kiapi::board::TeardropDefaults* teardrops = rules->mutable_teardrops();
1263
1264 teardrops->set_target_vias( bds.m_TeardropParamsList.m_TargetVias );
1265 teardrops->set_target_pth_pads( bds.m_TeardropParamsList.m_TargetPTHPads );
1266 teardrops->set_target_smd_pads( bds.m_TeardropParamsList.m_TargetSMDPads );
1267 teardrops->set_target_track_to_track( bds.m_TeardropParamsList.m_TargetTrack2Track );
1268 teardrops->set_use_round_shapes_only( bds.m_TeardropParamsList.m_UseRoundShapesOnly );
1269
1271
1272 for( int target = TARGET_ROUND; target <= TARGET_TRACK; ++target )
1273 {
1274 const TEARDROP_PARAMETERS* params = tdList.GetParameters( static_cast<TARGET_TD>( target ) );
1275 kiapi::board::TeardropTargetEntry* entry = teardrops->add_target_params();
1276
1278 static_cast<TARGET_TD>( target ) ) );
1279 entry->mutable_params()->set_enabled( params->m_Enabled );
1280 entry->mutable_params()->mutable_max_length()->set_value_nm( params->m_TdMaxLen );
1281 entry->mutable_params()->mutable_max_width()->set_value_nm( params->m_TdMaxWidth );
1282 entry->mutable_params()->set_best_length_ratio( params->m_BestLengthRatio );
1283 entry->mutable_params()->set_best_width_ratio( params->m_BestWidthRatio );
1284 entry->mutable_params()->set_width_to_size_filter_ratio( params->m_WidthtoSizeFilterRatio );
1285 entry->mutable_params()->set_curved_edges( params->m_CurvedEdges );
1286 entry->mutable_params()->set_allow_two_tracks( params->m_AllowUseTwoTracks );
1287 entry->mutable_params()->set_on_pads_in_zones( params->m_TdOnPadsInZones );
1288 }
1289
1290 kiapi::board::ViaProtectionDefaults* viaProtection = rules->mutable_via_protection();
1291
1292 viaProtection->set_tent_front( bds.m_TentViasFront );
1293 viaProtection->set_tent_back( bds.m_TentViasBack );
1294 viaProtection->set_cover_front( bds.m_CoverViasFront );
1295 viaProtection->set_cover_back( bds.m_CoverViasBack );
1296 viaProtection->set_plug_front( bds.m_PlugViasFront );
1297 viaProtection->set_plug_back( bds.m_PlugViasBack );
1298 viaProtection->set_cap( bds.m_CapVias );
1299 viaProtection->set_fill( bds.m_FillVias );
1300
1301 for( const auto& [errorCode, severity] : bds.m_DRCSeverities )
1302 {
1303 board::DrcSeveritySetting* setting = rules->add_severities();
1304 setting->set_rule_type(
1305 ToProtoEnum<PCB_DRC_CODE, board::DrcErrorType>( static_cast<PCB_DRC_CODE>( errorCode ) ) );
1306 setting->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( severity ) );
1307 }
1308
1309 for( const wxString& serialized : bds.m_DrcExclusions )
1310 {
1311 kiapi::board::DrcExclusion* exclusion = rules->add_exclusions();
1312 exclusion->mutable_marker()->mutable_id()->set_opaque_id( serialized.ToStdString() );
1313
1314 auto it = bds.m_DrcExclusionComments.find( serialized );
1315
1316 if( it != bds.m_DrcExclusionComments.end() )
1317 exclusion->set_comment( it->second.ToStdString() );
1318 }
1319
1320 response.set_custom_rules_status( CRS_NONE );
1321
1322 wxFileName fn = context()->GetBoard()->GetFileName();
1324 wxString rulesPath = context()->Prj().AbsolutePath( fn.GetFullName() );
1325
1326 if( !rulesPath.IsEmpty() && wxFileName::IsFileReadable( rulesPath ) )
1327 {
1328 wxFFile file( rulesPath, "r" );
1329 wxString content;
1330 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1331
1332 if( !file.IsOpened() )
1333 {
1334 response.set_custom_rules_status( CRS_INVALID );
1335 return response;
1336 }
1337
1338 file.ReadAll( &content );
1339 file.Close();
1340
1341 try
1342 {
1343 DRC_RULES_PARSER parser( content, "File" );
1344 parser.Parse( parsedRules, nullptr );
1345 response.set_custom_rules_status( CRS_VALID );
1346 }
1347 catch( const IO_ERROR& )
1348 {
1349 response.set_custom_rules_status( CRS_INVALID );
1350 }
1351 }
1352
1353 return response;
1354}
1355
1356
1359{
1360 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1361
1362 if( !documentValidation )
1363 return tl::unexpected( documentValidation.error() );
1364
1365 BOARD_DESIGN_SETTINGS newSettings( board()->GetDesignSettings() );
1366 const kiapi::board::BoardDesignRules& rules = aCtx.Request.rules();
1367
1368 if( rules.has_constraints() )
1369 {
1370 const kiapi::board::MinimumConstraints& constraints = rules.constraints();
1371
1372 newSettings.m_MinClearance = constraints.min_clearance().value_nm();
1373 newSettings.m_MinGrooveWidth = constraints.min_groove_width().value_nm();
1374 newSettings.m_MinConn = constraints.min_connection_width().value_nm();
1375 newSettings.m_TrackMinWidth = constraints.min_track_width().value_nm();
1376 newSettings.m_ViasMinAnnularWidth = constraints.min_via_annular_width().value_nm();
1377 newSettings.m_ViasMinSize = constraints.min_via_size().value_nm();
1378 newSettings.m_MinThroughDrill = constraints.min_through_drill().value_nm();
1379 newSettings.m_MicroViasMinSize = constraints.min_microvia_size().value_nm();
1380 newSettings.m_MicroViasMinDrill = constraints.min_microvia_drill().value_nm();
1381 newSettings.m_CopperEdgeClearance = constraints.copper_edge_clearance().value_nm();
1382 newSettings.m_HoleClearance = constraints.hole_clearance().value_nm();
1383 newSettings.m_HoleToHoleMin = constraints.hole_to_hole_min().value_nm();
1384 newSettings.m_SilkClearance = constraints.silk_clearance().value_nm();
1385 newSettings.m_MinResolvedSpokes = constraints.min_resolved_spokes();
1386 newSettings.m_MinSilkTextHeight = constraints.min_silk_text_height().value_nm();
1387 newSettings.m_MinSilkTextThickness = constraints.min_silk_text_thickness().value_nm();
1388 }
1389
1390 if( rules.has_predefined_sizes() )
1391 {
1392 newSettings.m_TrackWidthList.clear();
1393 newSettings.m_TrackWidthList.emplace_back( 0 );
1394
1395 for( const kiapi::board::PresetTrackWidth& track : rules.predefined_sizes().tracks() )
1396 newSettings.m_TrackWidthList.emplace_back( track.width().value_nm() );
1397
1398 newSettings.m_ViasDimensionsList.clear();
1399 newSettings.m_ViasDimensionsList.emplace_back( 0, 0 );
1400
1401 for( const kiapi::board::PresetViaDimension& via : rules.predefined_sizes().vias() )
1402 {
1403 newSettings.m_ViasDimensionsList.emplace_back( static_cast<int>( via.diameter().value_nm() ),
1404 static_cast<int>( via.drill().value_nm() ) );
1405 }
1406
1407 newSettings.m_DiffPairDimensionsList.clear();
1408 newSettings.m_DiffPairDimensionsList.emplace_back( 0, 0, 0 );
1409
1410 for( const kiapi::board::PresetDiffPairDimension& pair : rules.predefined_sizes().diff_pairs() )
1411 {
1412 newSettings.m_DiffPairDimensionsList.emplace_back(
1413 static_cast<int>( pair.width().value_nm() ),
1414 static_cast<int>( pair.gap().value_nm() ),
1415 static_cast<int>( pair.via_gap().value_nm() ) );
1416 }
1417 }
1418
1419 if( rules.has_solder_mask_paste() )
1420 {
1421 const kiapi::board::SolderMaskPasteDefaults& maskPaste = rules.solder_mask_paste();
1422
1423 newSettings.m_SolderMaskExpansion = maskPaste.mask_expansion().value_nm();
1424 newSettings.m_SolderMaskMinWidth = maskPaste.mask_min_width().value_nm();
1425 newSettings.m_SolderMaskToCopperClearance = maskPaste.mask_to_copper_clearance().value_nm();
1426 newSettings.m_SolderPasteMargin = maskPaste.paste_margin().value_nm();
1427 newSettings.m_SolderPasteMarginRatio = maskPaste.paste_margin_ratio();
1428 newSettings.m_AllowSoldermaskBridgesInFPs =
1429 maskPaste.allow_soldermask_bridges_in_footprints();
1430 }
1431
1432 if( rules.has_teardrops() )
1433 {
1434 const kiapi::board::TeardropDefaults& teardrops = rules.teardrops();
1435
1436 newSettings.m_TeardropParamsList.m_TargetVias = teardrops.target_vias();
1437 newSettings.m_TeardropParamsList.m_TargetPTHPads = teardrops.target_pth_pads();
1438 newSettings.m_TeardropParamsList.m_TargetSMDPads = teardrops.target_smd_pads();
1439 newSettings.m_TeardropParamsList.m_TargetTrack2Track = teardrops.target_track_to_track();
1440 newSettings.m_TeardropParamsList.m_UseRoundShapesOnly = teardrops.use_round_shapes_only();
1441
1442 for( const kiapi::board::TeardropTargetEntry& entry : teardrops.target_params() )
1443 {
1444 if( entry.target() == kiapi::board::TeardropTarget::TDT_UNKNOWN )
1445 continue;
1446
1448 entry.target() );
1449
1450 TEARDROP_PARAMETERS* params = newSettings.m_TeardropParamsList.GetParameters( target );
1451
1452 params->m_Enabled = entry.params().enabled();
1453 params->m_TdMaxLen = entry.params().max_length().value_nm();
1454 params->m_TdMaxWidth = entry.params().max_width().value_nm();
1455 params->m_BestLengthRatio = entry.params().best_length_ratio();
1456 params->m_BestWidthRatio = entry.params().best_width_ratio();
1457 params->m_WidthtoSizeFilterRatio = entry.params().width_to_size_filter_ratio();
1458 params->m_CurvedEdges = entry.params().curved_edges();
1459 params->m_AllowUseTwoTracks = entry.params().allow_two_tracks();
1460 params->m_TdOnPadsInZones = entry.params().on_pads_in_zones();
1461 }
1462 }
1463
1464 if( rules.has_via_protection() )
1465 {
1466 const kiapi::board::ViaProtectionDefaults& viaProtection = rules.via_protection();
1467
1468 newSettings.m_TentViasFront = viaProtection.tent_front();
1469 newSettings.m_TentViasBack = viaProtection.tent_back();
1470 newSettings.m_CoverViasFront = viaProtection.cover_front();
1471 newSettings.m_CoverViasBack = viaProtection.cover_back();
1472 newSettings.m_PlugViasFront = viaProtection.plug_front();
1473 newSettings.m_PlugViasBack = viaProtection.plug_back();
1474 newSettings.m_CapVias = viaProtection.cap();
1475 newSettings.m_FillVias = viaProtection.fill();
1476 }
1477
1478 if( rules.severities_size() > 0 )
1479 {
1480 newSettings.m_DRCSeverities.clear();
1481
1482 for( const kiapi::board::DrcSeveritySetting& severitySetting : rules.severities() )
1483 {
1484 PCB_DRC_CODE ruleType =
1485 FromProtoEnum<PCB_DRC_CODE, kiapi::board::DrcErrorType>( severitySetting.rule_type() );
1486
1487 const std::unordered_set<SEVERITY> permitted( { RPT_SEVERITY_ERROR, RPT_SEVERITY_WARNING, RPT_SEVERITY_IGNORE } );
1488 SEVERITY setting = FromProtoEnum<SEVERITY, kiapi::common::types::RuleSeverity>( severitySetting.severity() );
1489
1490 if( !permitted.contains( setting ) )
1491 {
1492 ApiResponseStatus e;
1493 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1494 e.set_error_message( fmt::format( "DRC severity must be error, warning, or ignore" ) );
1495 return tl::unexpected( e );
1496 }
1497
1498 newSettings.m_DRCSeverities[ruleType] = setting;
1499 }
1500 }
1501
1502 if( rules.exclusions_size() > 0 )
1503 {
1504 newSettings.m_DrcExclusions.clear();
1505 newSettings.m_DrcExclusionComments.clear();
1506
1507 for( const kiapi::board::DrcExclusion& exclusion : rules.exclusions() )
1508 {
1509 wxString serialized = wxString::FromUTF8( exclusion.marker().id().opaque_id() );
1510
1511 if( serialized.IsEmpty() )
1512 {
1513 ApiResponseStatus e;
1514 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1515 e.set_error_message( "DrcExclusion marker id must not be empty" );
1516 return tl::unexpected( e );
1517 }
1518
1519 newSettings.m_DrcExclusions.insert( serialized );
1520 newSettings.m_DrcExclusionComments[serialized] = wxString::FromUTF8( exclusion.comment() );
1521 }
1522 }
1523
1524 std::vector<BOARD_DESIGN_SETTINGS::VALIDATION_ERROR> errors = newSettings.ValidateDesignRules();
1525
1526 if( !errors.empty() )
1527 {
1528 const BOARD_DESIGN_SETTINGS::VALIDATION_ERROR& error = errors.front();
1529
1530 ApiResponseStatus e;
1531 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1532 e.set_error_message( fmt::format( "Invalid board design rules: {}: {}",
1533 error.setting_name.ToStdString(),
1534 error.error_message.ToStdString() ) );
1535 return tl::unexpected( e );
1536 }
1537
1538 board()->SetDesignSettings( newSettings );
1539
1540 if( frame() )
1541 {
1542 frame()->OnModify();
1544 }
1545
1546 HANDLER_CONTEXT<GetBoardDesignRules> getCtx = { aCtx.ClientName, GetBoardDesignRules() };
1547 *getCtx.Request.mutable_board() = aCtx.Request.board();
1548
1549 return handleGetBoardDesignRules( getCtx );
1550}
1551
1552
1555{
1556 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1557
1558 if( !documentValidation )
1559 return tl::unexpected( documentValidation.error() );
1560
1561 CustomRulesResponse response;
1562 response.set_status( CRS_NONE );
1563
1564 wxFileName fn = context()->GetBoard()->GetFileName();
1566 wxString rulesPath = context()->Prj().AbsolutePath( fn.GetFullName() );
1567
1568 if( rulesPath.IsEmpty() || !wxFileName::IsFileReadable( rulesPath ) )
1569 return response;
1570
1571 wxFFile file( rulesPath, "r" );
1572
1573 if( !file.IsOpened() )
1574 {
1575 response.set_status( CRS_INVALID );
1576 response.set_error_text( "Failed to open custom rules file" );
1577 return response;
1578 }
1579
1580 wxString content;
1581 file.ReadAll( &content );
1582 file.Close();
1583
1584 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1585
1586 try
1587 {
1588 DRC_RULES_PARSER parser( content, "File" );
1589 parser.Parse( parsedRules, nullptr );
1590 }
1591 catch( const IO_ERROR& ioe )
1592 {
1593 response.set_status( CRS_INVALID );
1594 response.set_error_text( ioe.What().ToStdString() );
1595 return response;
1596 }
1597
1598 for( const std::shared_ptr<DRC_RULE>& rule : parsedRules )
1599 {
1600 // TODO(JE) since we now need this for both here and the rules editor, maybe it's time
1601 // to just make comment parsing part of the parser?
1602 wxString text = DRC_RULE_LOADER::ExtractRuleText( content, rule->m_Name );
1603 wxString comment = DRC_RULE_LOADER::ExtractRuleComment( text );
1604
1605 kiapi::board::CustomRule* customRule = response.add_rules();
1606
1607 if( rule->m_Condition )
1608 customRule->set_condition( rule->m_Condition->GetExpression().ToUTF8() );
1609
1610 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
1611 {
1612 board::CustomRuleConstraint* constraintProto = customRule->add_constraints();
1613 constraint.ToProto( *constraintProto );
1614 }
1615
1616 customRule->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( rule->m_Severity ) );
1617 customRule->set_name( rule->m_Name.ToUTF8() );
1618
1619 if( rule->m_LayerSource.CmpNoCase( wxS( "outer" ) ) == 0 )
1620 {
1621 customRule->set_layer_mode( kiapi::board::CRLM_OUTER );
1622 }
1623 else if( rule->m_LayerSource.CmpNoCase( wxS( "inner" ) ) == 0 )
1624 {
1625 customRule->set_layer_mode( kiapi::board::CRLM_INNER );
1626 }
1627 else if( !rule->m_LayerSource.IsEmpty() )
1628 {
1629 int layer = LSET::NameToLayer( rule->m_LayerSource );
1630
1631 if( layer != UNDEFINED_LAYER && layer != UNSELECTED_LAYER && layer < PCB_LAYER_ID_COUNT )
1632 {
1633 customRule->set_single_layer(
1635 }
1636 }
1637
1638 if( !comment.IsEmpty() )
1639 customRule->set_comments( comment );
1640 }
1641
1642 response.set_status( CRS_VALID );
1643 return response;
1644}
1645
1646
1649{
1650 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1651
1652 if( !documentValidation )
1653 return tl::unexpected( documentValidation.error() );
1654
1655 wxFileName fn = context()->GetBoard()->GetFileName();
1657 wxString rulesPath = context()->Prj().AbsolutePath( fn.GetFullName() );
1658
1659 if( aCtx.Request.rules_size() == 0 )
1660 {
1661 if( wxFileName::FileExists( rulesPath ) )
1662 {
1663 if( !wxRemoveFile( rulesPath ) )
1664 {
1665 CustomRulesResponse response;
1666 response.set_status( CRS_INVALID );
1667 response.set_error_text( "Failed to remove custom rules file" );
1668 return response;
1669 }
1670 }
1671
1672 CustomRulesResponse response;
1673 response.set_status( CRS_NONE );
1674 return response;
1675 }
1676
1677 wxString rulesText;
1678 rulesText << "(version 1)\n";
1679
1680 for( const board::CustomRule& rule : aCtx.Request.rules() )
1681 {
1682 wxString serializationError;
1683 wxString serializedRule = DRC_RULE::FormatRuleFromProto( rule, &serializationError );
1684
1685 if( serializedRule.IsEmpty() )
1686 {
1687 CustomRulesResponse response;
1688 response.set_status( CRS_INVALID );
1689
1690 if( serializationError.IsEmpty() )
1691 response.set_error_text( "Failed to serialize custom rule" );
1692 else
1693 response.set_error_text( serializationError.ToUTF8() );
1694
1695 return response;
1696 }
1697
1698 rulesText << "\n" << serializedRule;
1699 }
1700
1701 // Validate generated file text before writing so callers get parser errors in response.
1702 try
1703 {
1704 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1705 DRC_RULES_PARSER parser( rulesText, "SetCustomDesignRules" );
1706 parser.Parse( parsedRules, nullptr );
1707 }
1708 catch( const IO_ERROR& ioe )
1709 {
1710 CustomRulesResponse response;
1711 response.set_status( CRS_INVALID );
1712 response.set_error_text( ioe.What().ToStdString() );
1713 return response;
1714 }
1715
1716 wxFFile file( rulesPath, "w" );
1717
1718 if( !file.IsOpened() )
1719 {
1720 CustomRulesResponse response;
1721 response.set_status( CRS_INVALID );
1722 response.set_error_text( "Failed to open custom rules file for writing" );
1723 return response;
1724 }
1725
1726 if( !file.Write( rulesText ) )
1727 {
1728 file.Close();
1729
1730 CustomRulesResponse response;
1731 response.set_status( CRS_INVALID );
1732 response.set_error_text( "Failed to write custom rules file" );
1733 return response;
1734 }
1735
1736 file.Close();
1737
1738 HANDLER_CONTEXT<GetCustomDesignRules> getCtx = { aCtx.ClientName, GetCustomDesignRules() };
1739 *getCtx.Request.mutable_board() = aCtx.Request.board();
1740 return handleGetCustomDesignRules( getCtx );
1741}
1742
1743
1746{
1747 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1748 !documentValidation )
1749 {
1750 return tl::unexpected( documentValidation.error() );
1751 }
1752
1753 VECTOR2I origin;
1754 const BOARD_DESIGN_SETTINGS& settings = board()->GetDesignSettings();
1755
1756 switch( aCtx.Request.type() )
1757 {
1758 case BOT_GRID:
1759 origin = settings.GetGridOrigin();
1760 break;
1761
1762 case BOT_DRILL:
1763 origin = settings.GetAuxOrigin();
1764 break;
1765
1766 default:
1767 case BOT_UNKNOWN:
1768 {
1769 ApiResponseStatus e;
1770 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1771 e.set_error_message( "Unexpected origin type" );
1772 return tl::unexpected( e );
1773 }
1774 }
1775
1776 types::Vector2 reply;
1777 PackVector2( reply, origin );
1778 return reply;
1779}
1780
1783{
1784 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1785 return tl::unexpected( *busy );
1786
1787 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1788 !documentValidation )
1789 {
1790 return tl::unexpected( documentValidation.error() );
1791 }
1792
1793 VECTOR2I origin = UnpackVector2( aCtx.Request.origin() );
1794
1795 switch( aCtx.Request.type() )
1796 {
1797 case BOT_GRID:
1798 {
1799 PCB_EDIT_FRAME* f = frame();
1800
1801 frame()->CallAfter( [f, origin]()
1802 {
1803 // gridSetOrigin takes ownership and frees this
1804 VECTOR2D* dorigin = new VECTOR2D( origin );
1805 TOOL_MANAGER* mgr = f->GetToolManager();
1806 mgr->RunAction( PCB_ACTIONS::gridSetOrigin, dorigin );
1807 f->Refresh();
1808 } );
1809 break;
1810 }
1811
1812 case BOT_DRILL:
1813 {
1814 PCB_EDIT_FRAME* f = frame();
1815
1816 frame()->CallAfter( [f, origin]()
1817 {
1818 TOOL_MANAGER* mgr = f->GetToolManager();
1819 mgr->RunAction( PCB_ACTIONS::drillSetOrigin, origin );
1820 f->Refresh();
1821 } );
1822 break;
1823 }
1824
1825 default:
1826 case BOT_UNKNOWN:
1827 {
1828 ApiResponseStatus e;
1829 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1830 e.set_error_message( "Unexpected origin type" );
1831 return tl::unexpected( e );
1832 }
1833 }
1834
1835 return Empty();
1836}
1837
1838
1841{
1842 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1843 !documentValidation )
1844 {
1845 return tl::unexpected( documentValidation.error() );
1846 }
1847
1848 BoardLayerNameResponse response;
1849
1851
1852 response.set_name( board()->GetLayerName( id ) );
1853
1854 return response;
1855}
1856
1857
1860{
1861 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1862 return tl::unexpected( *busy );
1863
1864 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1865 {
1866 ApiResponseStatus e;
1867 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
1868 e.set_status( ApiStatusCode::AS_UNHANDLED );
1869 return tl::unexpected( e );
1870 }
1871
1872 GetBoundingBoxResponse response;
1873 bool includeText = aCtx.Request.mode() == BoundingBoxMode::BBM_ITEM_AND_CHILD_TEXT;
1874
1875 for( const types::KIID& idMsg : aCtx.Request.items() )
1876 {
1877 KIID id( idMsg.value() );
1878 std::optional<BOARD_ITEM*> optItem = getItemById( id );
1879
1880 if( !optItem )
1881 continue;
1882
1883 BOARD_ITEM* item = *optItem;
1884 BOX2I bbox;
1885
1886 if( item->Type() == PCB_FOOTPRINT_T )
1887 bbox = static_cast<FOOTPRINT*>( item )->GetBoundingBox( includeText );
1888 else
1889 bbox = item->GetBoundingBox();
1890
1891 response.add_items()->set_value( idMsg.value() );
1892 PackBox2( *response.add_boxes(), bbox );
1893 }
1894
1895 return response;
1896}
1897
1898
1901{
1902 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1903 !documentValidation )
1904 {
1905 return tl::unexpected( documentValidation.error() );
1906 }
1907
1908 PadShapeAsPolygonResponse response;
1910
1911 for( const types::KIID& padRequest : aCtx.Request.pads() )
1912 {
1913 KIID id( padRequest.value() );
1914 std::optional<BOARD_ITEM*> optPad = getItemById( id );
1915
1916 if( !optPad || ( *optPad )->Type() != PCB_PAD_T )
1917 continue;
1918
1919 response.add_pads()->set_value( padRequest.value() );
1920
1921 PAD* pad = static_cast<PAD*>( *optPad );
1922 SHAPE_POLY_SET poly;
1923 pad->TransformShapeToPolygon( poly, pad->Padstack().EffectiveLayerFor( layer ), 0,
1924 pad->GetMaxError(), ERROR_INSIDE );
1925
1926 types::PolygonWithHoles* polyMsg = response.mutable_polygons()->Add();
1927 PackPolyLine( *polyMsg->mutable_outline(), poly.COutline( 0 ) );
1928 }
1929
1930 return response;
1931}
1932
1933
1936{
1937 using board::types::BoardLayer;
1938
1939 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1940 !documentValidation )
1941 {
1942 return tl::unexpected( documentValidation.error() );
1943 }
1944
1945 PadstackPresenceResponse response;
1946
1947 LSET layers;
1948
1949 for( const int layer : aCtx.Request.layers() )
1950 layers.set( FromProtoEnum<PCB_LAYER_ID, BoardLayer>( static_cast<BoardLayer>( layer ) ) );
1951
1952 for( const types::KIID& padRequest : aCtx.Request.items() )
1953 {
1954 KIID id( padRequest.value() );
1955 std::optional<BOARD_ITEM*> optItem = getItemById( id );
1956
1957 if( !optItem )
1958 continue;
1959
1960 switch( ( *optItem )->Type() )
1961 {
1962 case PCB_PAD_T:
1963 {
1964 PAD* pad = static_cast<PAD*>( *optItem );
1965
1966 for( PCB_LAYER_ID layer : layers )
1967 {
1968 PadstackPresenceEntry* entry = response.add_entries();
1969 entry->mutable_item()->set_value( pad->m_Uuid.AsStdString() );
1970 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
1971 entry->set_presence( pad->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
1972 }
1973
1974 break;
1975 }
1976
1977 case PCB_VIA_T:
1978 {
1979 PCB_VIA* via = static_cast<PCB_VIA*>( *optItem );
1980
1981 for( PCB_LAYER_ID layer : layers )
1982 {
1983 PadstackPresenceEntry* entry = response.add_entries();
1984 entry->mutable_item()->set_value( via->m_Uuid.AsStdString() );
1985 entry->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( layer ) );
1986 entry->set_presence( via->FlashLayer( layer ) ? PSP_PRESENT : PSP_NOT_PRESENT );
1987 }
1988
1989 break;
1990 }
1991
1992 default:
1993 break;
1994 }
1995 }
1996
1997 return response;
1998}
1999
2000
2003{
2004 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2005
2006 if( !documentValidation )
2007 return tl::unexpected( documentValidation.error() );
2008
2009 const TITLE_BLOCK& block = board()->GetTitleBlock();
2010
2011 types::TitleBlockInfo response;
2012
2013 response.set_title( block.GetTitle().ToUTF8() );
2014 response.set_date( block.GetDate().ToUTF8() );
2015 response.set_revision( block.GetRevision().ToUTF8() );
2016 response.set_company( block.GetCompany().ToUTF8() );
2017 response.set_comment1( block.GetComment( 0 ).ToUTF8() );
2018 response.set_comment2( block.GetComment( 1 ).ToUTF8() );
2019 response.set_comment3( block.GetComment( 2 ).ToUTF8() );
2020 response.set_comment4( block.GetComment( 3 ).ToUTF8() );
2021 response.set_comment5( block.GetComment( 4 ).ToUTF8() );
2022 response.set_comment6( block.GetComment( 5 ).ToUTF8() );
2023 response.set_comment7( block.GetComment( 6 ).ToUTF8() );
2024 response.set_comment8( block.GetComment( 7 ).ToUTF8() );
2025 response.set_comment9( block.GetComment( 8 ).ToUTF8() );
2026
2027 return response;
2028}
2029
2030
2032{
2033 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2034
2035 if( !documentValidation )
2036 return tl::unexpected( documentValidation.error() );
2037
2038 if( !aCtx.Request.has_title_block() )
2039 {
2040 ApiResponseStatus e;
2041 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2042 e.set_error_message( "SetTitleBlockInfo requires title_block" );
2043 return tl::unexpected( e );
2044 }
2045
2046 TITLE_BLOCK& block = board()->GetTitleBlock();
2047
2048 const types::TitleBlockInfo& request = aCtx.Request.title_block();
2049
2050 block.SetTitle( wxString::FromUTF8( request.title() ) );
2051 block.SetDate( wxString::FromUTF8( request.date() ) );
2052 block.SetRevision( wxString::FromUTF8( request.revision() ) );
2053 block.SetCompany( wxString::FromUTF8( request.company() ) );
2054 block.SetComment( 0, wxString::FromUTF8( request.comment1() ) );
2055 block.SetComment( 1, wxString::FromUTF8( request.comment2() ) );
2056 block.SetComment( 2, wxString::FromUTF8( request.comment3() ) );
2057 block.SetComment( 3, wxString::FromUTF8( request.comment4() ) );
2058 block.SetComment( 4, wxString::FromUTF8( request.comment5() ) );
2059 block.SetComment( 5, wxString::FromUTF8( request.comment6() ) );
2060 block.SetComment( 6, wxString::FromUTF8( request.comment7() ) );
2061 block.SetComment( 7, wxString::FromUTF8( request.comment8() ) );
2062 block.SetComment( 8, wxString::FromUTF8( request.comment9() ) );
2063
2064 if( frame() )
2065 {
2066 frame()->OnModify();
2068 }
2069
2070 return Empty();
2071}
2072
2073
2076{
2077 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2078
2079 if( !documentValidation )
2080 return tl::unexpected( documentValidation.error() );
2081
2082 ExpandTextVariablesResponse reply;
2083 BOARD* board = this->board();
2084
2085 std::function<bool( wxString* )> textResolver =
2086 [&]( wxString* token ) -> bool
2087 {
2088 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
2089 return board->ResolveTextVar( token, 0 );
2090 };
2091
2092 for( const std::string& textMsg : aCtx.Request.text() )
2093 {
2094 wxString text = ExpandTextVars( wxString::FromUTF8( textMsg ), &textResolver );
2095 reply.add_text( text.ToUTF8() );
2096 }
2097
2098 return reply;
2099}
2100
2101
2104{
2105 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "InteractiveMoveItems" ) )
2106 return tl::unexpected( *headless );
2107
2108 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2109 return tl::unexpected( *busy );
2110
2111 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2112
2113 if( !documentValidation )
2114 return tl::unexpected( documentValidation.error() );
2115
2116 TOOL_MANAGER* mgr = toolManager();
2117 std::vector<EDA_ITEM*> toSelect;
2118
2119 for( const kiapi::common::types::KIID& id : aCtx.Request.items() )
2120 {
2121 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
2122 toSelect.emplace_back( static_cast<EDA_ITEM*>( *item ) );
2123 }
2124
2125 if( toSelect.empty() )
2126 {
2127 ApiResponseStatus e;
2128 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2129 e.set_error_message( fmt::format( "None of the given items exist on the board",
2130 aCtx.Request.board().board_filename() ) );
2131 return tl::unexpected( e );
2132 }
2133
2134 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
2135 selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() );
2136
2138 mgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &toSelect );
2139
2140 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
2141 mgr->PostAPIAction( PCB_ACTIONS::move, commit );
2142
2143 return Empty();
2144}
2145
2146
2148{
2149 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2150
2151 if( !documentValidation )
2152 return tl::unexpected( documentValidation.error() );
2153
2154 NetsResponse response;
2155 BOARD* board = this->board();
2156
2157 std::set<wxString> netclassFilter;
2158
2159 for( const std::string& nc : aCtx.Request.netclass_filter() )
2160 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
2161
2162 for( NETINFO_ITEM* net : board->GetNetInfo() )
2163 {
2164 NETCLASS* nc = net->GetNetClass();
2165
2166 if( !netclassFilter.empty() && nc )
2167 {
2168 bool inClass = false;
2169
2170 for( const wxString& filter : netclassFilter )
2171 {
2172 if( nc->ContainsNetclassWithName( filter ) )
2173 {
2174 inClass = true;
2175 break;
2176 }
2177 }
2178
2179 if( !inClass )
2180 continue;
2181 }
2182
2183 board::types::Net* netProto = response.add_nets();
2184 netProto->set_name( net->GetNetname() );
2185 netProto->mutable_code()->set_value( net->GetNetCode() );
2186 }
2187
2188 return response;
2189}
2190
2191
2194{
2195 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2196 return tl::unexpected( *busy );
2197
2198 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
2199 {
2200 ApiResponseStatus e;
2201 e.set_status( ApiStatusCode::AS_UNHANDLED );
2202 return tl::unexpected( e );
2203 }
2204
2205 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
2206 const bool filterByType = aCtx.Request.types_size() > 0;
2207
2208 if( filterByType && types.empty() )
2209 {
2210 ApiResponseStatus e;
2211 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2212 e.set_error_message( "none of the requested types are valid for a Board object" );
2213 return tl::unexpected( e );
2214 }
2215
2216 std::set<KICAD_T> typeFilter( types.begin(), types.end() );
2217 std::vector<BOARD_CONNECTED_ITEM*> sourceItems;
2218
2219 for( const types::KIID& id : aCtx.Request.items() )
2220 {
2221 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
2222 {
2223 if( BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( *item ) )
2224 sourceItems.emplace_back( connected );
2225 }
2226 }
2227
2228 if( sourceItems.empty() )
2229 {
2230 ApiResponseStatus e;
2231 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2232 e.set_error_message( "none of the requested IDs were found or valid connected items" );
2233 return tl::unexpected( e );
2234 }
2235
2236 GetItemsResponse response;
2237 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
2238 std::set<KIID> insertedItems;
2239
2240 for( BOARD_CONNECTED_ITEM* source : sourceItems )
2241 {
2242 for( BOARD_CONNECTED_ITEM* connected : conn->GetConnectedItems( source ) )
2243 {
2244 if( filterByType && !typeFilter.contains( connected->Type() ) )
2245 continue;
2246
2247 if( !insertedItems.insert( connected->m_Uuid ).second )
2248 continue;
2249
2250 connected->Serialize( *response.add_items() );
2251 }
2252 }
2253
2254 response.set_status( ItemRequestStatus::IRS_OK );
2255 return response;
2256}
2257
2258
2259std::vector<KICAD_T> API_HANDLER_PCB::parseRequestedItemTypes( const google::protobuf::RepeatedField<int>& aTypes )
2260{
2261 std::vector<KICAD_T> types;
2262
2263 for( int typeRaw : aTypes )
2264 {
2265 auto typeMessage = static_cast<common::types::KiCadObjectType>( typeRaw );
2266 KICAD_T type = FromProtoEnum<KICAD_T>( typeMessage );
2267
2268 if( type != TYPE_NOT_INIT )
2269 types.emplace_back( type );
2270 }
2271
2272 return types;
2273}
2274
2275
2277 const HANDLER_CONTEXT<GetItemsByNet>& aCtx )
2278{
2279 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2280 return tl::unexpected( *busy );
2281
2282 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
2283 {
2284 ApiResponseStatus e;
2285 e.set_status( ApiStatusCode::AS_UNHANDLED );
2286 return tl::unexpected( e );
2287 }
2288
2289 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
2290 const bool filterByType = aCtx.Request.types_size() > 0;
2291
2292 if( filterByType && types.empty() )
2293 {
2294 ApiResponseStatus e;
2295 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2296 e.set_error_message( "none of the requested types are valid for a Board object" );
2297 return tl::unexpected( e );
2298 }
2299
2300 if( !filterByType )
2302
2303 GetItemsResponse response;
2304 BOARD* board = this->board();
2305 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
2306 std::set<KIID> insertedItems;
2307
2308 const NETINFO_LIST& nets = board->GetNetInfo();
2309
2310 for( const board::types::Net& net : aCtx.Request.nets() )
2311 {
2312 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
2313
2314 if( !netInfo )
2315 continue;
2316
2317 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( netInfo->GetNetCode(), types ) )
2318 {
2319 if( !insertedItems.insert( item->m_Uuid ).second )
2320 continue;
2321
2322 item->Serialize( *response.add_items() );
2323 }
2324 }
2325
2326 response.set_status( ItemRequestStatus::IRS_OK );
2327 return response;
2328}
2329
2330
2333{
2334 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2335 return tl::unexpected( *busy );
2336
2337 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
2338 {
2339 ApiResponseStatus e;
2340 e.set_status( ApiStatusCode::AS_UNHANDLED );
2341 return tl::unexpected( e );
2342 }
2343
2344 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
2345 const bool filterByType = aCtx.Request.types_size() > 0;
2346
2347 if( filterByType && types.empty() )
2348 {
2349 ApiResponseStatus e;
2350 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2351 e.set_error_message( "none of the requested types are valid for a Board object" );
2352 return tl::unexpected( e );
2353 }
2354
2355 if( !filterByType )
2357
2358 std::set<wxString> requestedClasses;
2359
2360 for( const std::string& netClass : aCtx.Request.net_classes() )
2361 requestedClasses.insert( wxString( netClass.c_str(), wxConvUTF8 ) );
2362
2363 GetItemsResponse response;
2364 BOARD* board = this->board();
2365 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
2366 std::set<KIID> insertedItems;
2367
2368 for( NETINFO_ITEM* net : board->GetNetInfo() )
2369 {
2370 if( !net )
2371 continue;
2372
2373 NETCLASS* nc = net->GetNetClass();
2374
2375 if( !requestedClasses.empty() )
2376 {
2377 if( !nc )
2378 continue;
2379
2380 bool inClass = false;
2381
2382 for( const wxString& filter : requestedClasses )
2383 {
2384 if( nc->ContainsNetclassWithName( filter ) )
2385 {
2386 inClass = true;
2387 break;
2388 }
2389 }
2390
2391 if( !inClass )
2392 continue;
2393 }
2394
2395 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( net->GetNetCode(), types ) )
2396 {
2397 if( !insertedItems.insert( item->m_Uuid ).second )
2398 continue;
2399
2400 item->Serialize( *response.add_items() );
2401 }
2402 }
2403
2404 response.set_status( ItemRequestStatus::IRS_OK );
2405 return response;
2406}
2407
2408
2411{
2412 NetClassForNetsResponse response;
2413
2414 BOARD* board = this->board();
2415 const NETINFO_LIST& nets = board->GetNetInfo();
2416 google::protobuf::Any any;
2417
2418 for( const board::types::Net& net : aCtx.Request.net() )
2419 {
2420 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
2421
2422 if( !netInfo )
2423 continue;
2424
2425 netInfo->GetNetClass()->Serialize( any );
2426 auto [pair, rc] = response.mutable_classes()->insert( { net.name(), {} } );
2427 any.UnpackTo( &pair->second );
2428 }
2429
2430 return response;
2431}
2432
2433
2435{
2436 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2437 return tl::unexpected( *busy );
2438
2439 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2440
2441 if( !documentValidation )
2442 return tl::unexpected( documentValidation.error() );
2443
2444 if( aCtx.Request.zones().empty() )
2445 {
2446 TOOL_MANAGER* mgr = toolManager();
2447 frame()->CallAfter( [mgr]()
2448 {
2450 } );
2451 }
2452 else
2453 {
2454 // TODO
2455 ApiResponseStatus e;
2456 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
2457 return tl::unexpected( e );
2458 }
2459
2460 return Empty();
2461}
2462
2463
2466{
2467 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2468
2469 if( !documentValidation )
2470 return tl::unexpected( documentValidation.error() );
2471
2472 SavedDocumentResponse response;
2473 response.mutable_document()->CopyFrom( aCtx.Request.document() );
2474
2475 CLIPBOARD_IO io;
2476 io.SetWriter(
2477 [&]( const wxString& aData )
2478 {
2479 response.set_contents( aData.ToUTF8() );
2480 } );
2481
2482 io.SaveBoard( wxEmptyString, board(), nullptr );
2483
2484 return response;
2485}
2486
2487
2490{
2491 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SaveSelectionToString" ) )
2492 return tl::unexpected( *headless );
2493
2494 SavedSelectionResponse response;
2495
2496 TOOL_MANAGER* mgr = toolManager();
2497 PCB_SELECTION_TOOL* selectionTool = mgr->GetTool<PCB_SELECTION_TOOL>();
2498 PCB_SELECTION& selection = selectionTool->GetSelection();
2499
2500 CLIPBOARD_IO io;
2501 io.SetWriter(
2502 [&]( const wxString& aData )
2503 {
2504 response.set_contents( aData.ToUTF8() );
2505 } );
2506
2507 io.SetBoard( board() );
2508 io.SaveSelection( selection, false );
2509
2510 return response;
2511}
2512
2513
2516{
2517 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2518 return tl::unexpected( *busy );
2519
2520 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
2521
2522 if( !documentValidation )
2523 return tl::unexpected( documentValidation.error() );
2524
2525 CreateItemsResponse response;
2526 return response;
2527}
2528
2529
2532{
2533 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetVisibleLayers" ) )
2534 return tl::unexpected( *headless );
2535
2536 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2537
2538 if( !documentValidation )
2539 return tl::unexpected( documentValidation.error() );
2540
2541 BoardLayers response;
2542
2543 for( PCB_LAYER_ID layer : board()->GetVisibleLayers() )
2544 response.add_layers( ToProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( layer ) );
2545
2546 return response;
2547}
2548
2549
2552{
2553 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetVisibleLayers" ) )
2554 return tl::unexpected( *headless );
2555
2556 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2557 return tl::unexpected( *busy );
2558
2559 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2560
2561 if( !documentValidation )
2562 return tl::unexpected( documentValidation.error() );
2563
2564 LSET visible;
2565 LSET enabled = board()->GetEnabledLayers();
2566
2567 for( int layerIdx : aCtx.Request.layers() )
2568 {
2569 PCB_LAYER_ID layer =
2570 FromProtoEnum<PCB_LAYER_ID>( static_cast<board::types::BoardLayer>( layerIdx ) );
2571
2572 if( enabled.Contains( layer ) )
2573 visible.set( layer );
2574 }
2575
2576 board()->SetVisibleLayers( visible );
2579 frame()->Refresh();
2580 return Empty();
2581}
2582
2583
2586{
2587 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetActiveLayer" ) )
2588 return tl::unexpected( *headless );
2589
2590 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2591
2592 if( !documentValidation )
2593 return tl::unexpected( documentValidation.error() );
2594
2595 BoardLayerResponse response;
2596 response.set_layer(
2598
2599 return response;
2600}
2601
2602
2605{
2606 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetActiveLayer" ) )
2607 return tl::unexpected( *headless );
2608
2609 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2610 return tl::unexpected( *busy );
2611
2612 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2613
2614 if( !documentValidation )
2615 return tl::unexpected( documentValidation.error() );
2616
2617 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( aCtx.Request.layer() );
2618
2619 if( !board()->GetEnabledLayers().Contains( layer ) )
2620 {
2621 ApiResponseStatus err;
2622 err.set_status( ApiStatusCode::AS_BAD_REQUEST );
2623 err.set_error_message( fmt::format( "Layer {} is not a valid layer for the given board",
2624 magic_enum::enum_name( layer ) ) );
2625 return tl::unexpected( err );
2626 }
2627
2628 frame()->SetActiveLayer( layer );
2629 return Empty();
2630}
2631
2632
2635{
2636 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetBoardEditorAppearanceSettings" ) )
2637 return tl::unexpected( *headless );
2638
2639 BoardEditorAppearanceSettings reply;
2640
2641 // TODO: might be nice to put all these things in one place and have it derive SERIALIZABLE
2642
2643 const PCB_DISPLAY_OPTIONS& displayOptions = frame()->GetDisplayOptions();
2644
2645 reply.set_inactive_layer_display( ToProtoEnum<HIGH_CONTRAST_MODE, InactiveLayerDisplayMode>(
2646 displayOptions.m_ContrastModeDisplay ) );
2647 reply.set_net_color_display(
2649
2650 reply.set_board_flip( frame()->GetCanvas()->GetView()->IsMirroredX()
2651 ? BoardFlipMode::BFM_FLIPPED_X
2652 : BoardFlipMode::BFM_NORMAL );
2653
2654 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
2655
2656 reply.set_ratsnest_display( ToProtoEnum<RATSNEST_MODE, RatsnestDisplayMode>(
2657 editorSettings->m_Display.m_RatsnestMode ) );
2658
2659 return reply;
2660}
2661
2662
2665{
2666 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetBoardEditorAppearanceSettings" ) )
2667 return tl::unexpected( *headless );
2668
2669 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2670 return tl::unexpected( *busy );
2671
2673 KIGFX::PCB_VIEW* view = frame()->GetCanvas()->GetView();
2674 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
2675 const BoardEditorAppearanceSettings& newSettings = aCtx.Request.settings();
2676
2677 options.m_ContrastModeDisplay =
2678 FromProtoEnum<HIGH_CONTRAST_MODE>( newSettings.inactive_layer_display() );
2679 options.m_NetColorMode =
2680 FromProtoEnum<NET_COLOR_MODE>( newSettings.net_color_display() );
2681
2682 bool flip = newSettings.board_flip() == BoardFlipMode::BFM_FLIPPED_X;
2683
2684 if( flip != view->IsMirroredX() )
2685 {
2686 view->SetMirror( !view->IsMirroredX(), view->IsMirroredY() );
2687 view->RecacheAllItems();
2688 }
2689
2690 editorSettings->m_Display.m_RatsnestMode =
2691 FromProtoEnum<RATSNEST_MODE>( newSettings.ratsnest_display() );
2692
2693 frame()->SetDisplayOptions( options );
2695 frame()->GetCanvas()->Refresh();
2696
2697 return Empty();
2698}
2699
2700
2703{
2704 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2705 return tl::unexpected( *busy );
2706
2707 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2708
2709 if( !documentValidation )
2710 return tl::unexpected( documentValidation.error() );
2711
2712 SEVERITY severity = FromProtoEnum<SEVERITY>( aCtx.Request.severity() );
2713 int layer = severity == RPT_SEVERITY_WARNING ? LAYER_DRC_WARNING : LAYER_DRC_ERROR;
2715
2716 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( code );
2717
2718 drcItem->SetErrorMessage( wxString::FromUTF8( aCtx.Request.message() ) );
2719
2720 RC_ITEM::KIIDS ids;
2721
2722 for( const auto& id : aCtx.Request.items() )
2723 ids.emplace_back( KIID( id.value() ) );
2724
2725 if( !ids.empty() )
2726 drcItem->SetItems( ids );
2727
2728 const auto& pos = aCtx.Request.position();
2729 VECTOR2I position( static_cast<int>( pos.x_nm() ), static_cast<int>( pos.y_nm() ) );
2730
2731 PCB_MARKER* marker = new PCB_MARKER( drcItem, position, layer );
2732
2733 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
2734 commit->Add( marker );
2735 commit->Push( wxS( "API injected DRC marker" ) );
2736
2737 InjectDrcErrorResponse response;
2738 response.mutable_marker()->set_value( marker->GetUUID().AsStdString() );
2739
2740 return response;
2741}
2742
2743
2744std::optional<ApiResponseStatus> ValidateUnitsInchMm( types::Units aUnits,
2745 const std::string& aCommandName )
2746{
2747 if( aUnits == types::Units::U_INCH || aUnits == types::Units::U_MM
2748 || aUnits == types::Units::U_UNKNOWN )
2749 {
2750 return std::nullopt;
2751 }
2752
2753 ApiResponseStatus e;
2754 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2755 e.set_error_message( fmt::format( "{} supports only inch and mm units", aCommandName ) );
2756 return e;
2757}
2758
2759
2760std::optional<ApiResponseStatus>
2761ValidatePaginationModeForSingleOrPerFile( kiapi::board::jobs::BoardJobPaginationMode aMode,
2762 const std::string& aCommandName )
2763{
2764 if( aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_UNKNOWN
2765 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_ALL_LAYERS_ONE_PAGE
2766 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_EACH_LAYER_OWN_FILE )
2767 {
2768 return std::nullopt;
2769 }
2770
2771 ApiResponseStatus e;
2772 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2773 e.set_error_message( fmt::format( "{} does not support EACH_LAYER_OWN_PAGE pagination mode",
2774 aCommandName ) );
2775 return e;
2776}
2777
2778
2779std::optional<ApiResponseStatus> ApplyBoardPlotSettings( const BoardPlotSettings& aSettings,
2780 JOB_EXPORT_PCB_PLOT& aJob )
2781{
2782 for( int layer : aSettings.layers() )
2783 {
2785 static_cast<board::types::BoardLayer>( layer ) );
2786
2787 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2788 {
2789 ApiResponseStatus e;
2790 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2791 e.set_error_message( "Board plot settings contain an invalid layer" );
2792 return e;
2793 }
2794
2795 aJob.m_plotLayerSequence.push_back( layerId );
2796 }
2797
2798 for( int layer : aSettings.common_layers() )
2799 {
2801 static_cast<board::types::BoardLayer>( layer ) );
2802
2803 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2804 {
2805 ApiResponseStatus e;
2806 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2807 e.set_error_message( "Board plot settings contain an invalid common layer" );
2808 return e;
2809 }
2810
2811 aJob.m_plotOnAllLayersSequence.push_back( layerId );
2812 }
2813
2814 aJob.m_colorTheme = wxString::FromUTF8( aSettings.color_theme() );
2815 aJob.m_drawingSheet = wxString::FromUTF8( aSettings.drawing_sheet() );
2816 aJob.m_variant = wxString::FromUTF8( aSettings.variant() );
2817
2818 aJob.m_mirror = aSettings.mirror();
2819 aJob.m_blackAndWhite = aSettings.black_and_white();
2820 aJob.m_negative = aSettings.negative();
2821 aJob.m_scale = aSettings.scale();
2822
2823 aJob.m_sketchPadsOnFabLayers = aSettings.sketch_pads_on_fab_layers();
2824 aJob.m_hideDNPFPsOnFabLayers = aSettings.hide_dnp_footprints_on_fab_layers();
2825 aJob.m_sketchDNPFPsOnFabLayers = aSettings.sketch_dnp_footprints_on_fab_layers();
2826 aJob.m_crossoutDNPFPsOnFabLayers = aSettings.crossout_dnp_footprints_on_fab_layers();
2827
2828 aJob.m_plotFootprintValues = aSettings.plot_footprint_values();
2829 aJob.m_plotRefDes = aSettings.plot_reference_designators();
2830 aJob.m_plotDrawingSheet = aSettings.plot_drawing_sheet();
2831 aJob.m_subtractSolderMaskFromSilk = aSettings.subtract_solder_mask_from_silk();
2832 aJob.m_plotPadNumbers = aSettings.plot_pad_numbers();
2833
2834 aJob.m_drillShapeOption = FromProtoEnum<DRILL_MARKS>( aSettings.drill_marks() );
2835
2836 aJob.m_useDrillOrigin = aSettings.use_drill_origin();
2837 aJob.m_checkZonesBeforePlot = aSettings.check_zones_before_plot();
2838
2839 return std::nullopt;
2840}
2841
2842
2844{
2845 types::RunJobResponse response;
2846 WX_STRING_REPORTER reporter;
2847
2848 if( !aContext || !aContext->GetKiway() )
2849 {
2850 response.set_status( types::JobStatus::JS_ERROR );
2851 response.set_message( "Internal error" );
2852 return response;
2853 wxCHECK_MSG( false, response, "context missing valid kiway in ExecuteBoardJob?" );
2854 }
2855
2856 int exitCode = aContext->GetKiway()->ProcessJob( KIWAY::FACE_PCB, &aJob, &reporter );
2857
2858 for( const JOB_OUTPUT& output : aJob.GetOutputs() )
2859 response.add_output_path( output.m_outputPath.ToUTF8() );
2860
2861 if( exitCode == 0 )
2862 {
2863 response.set_status( types::JobStatus::JS_SUCCESS );
2864 return response;
2865 }
2866
2867 response.set_status( types::JobStatus::JS_ERROR );
2868 response.set_message( fmt::format( "Board export job '{}' failed with exit code {}: {}",
2869 aJob.GetType(), exitCode,
2870 reporter.GetMessages().ToStdString() ) );
2871 return response;
2872}
2873
2874
2877{
2878 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2879 return tl::unexpected( *busy );
2880
2881 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2882
2883 if( !documentValidation )
2884 return tl::unexpected( documentValidation.error() );
2885
2888 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2889
2891
2892 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2893 job.m_3dparams.m_NetFilter = wxString::FromUTF8( aCtx.Request.net_filter() );
2894 job.m_3dparams.m_ComponentFilter = wxString::FromUTF8( aCtx.Request.component_filter() );
2895
2896 job.m_hasUserOrigin = aCtx.Request.has_user_origin();
2897 job.m_3dparams.m_Origin = VECTOR2D( aCtx.Request.origin().x_nm(), aCtx.Request.origin().y_nm() );
2898
2899 job.m_3dparams.m_Overwrite = aCtx.Request.overwrite();
2900 job.m_3dparams.m_UseGridOrigin = aCtx.Request.use_grid_origin();
2901 job.m_3dparams.m_UseDrillOrigin = aCtx.Request.use_drill_origin();
2902 job.m_3dparams.m_UseDefinedOrigin = aCtx.Request.use_defined_origin() || aCtx.Request.has_user_origin();
2903 job.m_3dparams.m_UsePcbCenterOrigin = aCtx.Request.use_pcb_center_origin();
2904
2905 job.m_3dparams.m_IncludeUnspecified = aCtx.Request.include_unspecified();
2906 job.m_3dparams.m_IncludeDNP = aCtx.Request.include_dnp();
2907 job.m_3dparams.m_SubstModels = aCtx.Request.substitute_models();
2908
2909 job.m_3dparams.m_BoardOutlinesChainingEpsilon = aCtx.Request.board_outlines_chaining_epsilon();
2910 job.m_3dparams.m_BoardOnly = aCtx.Request.board_only();
2911 job.m_3dparams.m_CutViasInBody = aCtx.Request.cut_vias_in_body();
2912 job.m_3dparams.m_ExportBoardBody = aCtx.Request.export_board_body();
2913 job.m_3dparams.m_ExportComponents = aCtx.Request.export_components();
2914 job.m_3dparams.m_ExportTracksVias = aCtx.Request.export_tracks_and_vias();
2915 job.m_3dparams.m_ExportPads = aCtx.Request.export_pads();
2916 job.m_3dparams.m_ExportZones = aCtx.Request.export_zones();
2917 job.m_3dparams.m_ExportInnerCopper = aCtx.Request.export_inner_copper();
2918 job.m_3dparams.m_ExportSilkscreen = aCtx.Request.export_silkscreen();
2919 job.m_3dparams.m_ExportSoldermask = aCtx.Request.export_soldermask();
2920 job.m_3dparams.m_FuseShapes = aCtx.Request.fuse_shapes();
2921 job.m_3dparams.m_FillAllVias = aCtx.Request.fill_all_vias();
2922 job.m_3dparams.m_OptimizeStep = aCtx.Request.optimize_step();
2923 job.m_3dparams.m_ExtraPadThickness = aCtx.Request.extra_pad_thickness();
2924
2926
2927 job.m_vrmlModelDir = wxString::FromUTF8( aCtx.Request.vrml_model_dir() );
2928 job.m_vrmlRelativePaths = aCtx.Request.vrml_relative_paths();
2929
2930 return ExecuteBoardJob( context(), job );
2931}
2932
2933
2936{
2937 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2938 return tl::unexpected( *busy );
2939
2940 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2941
2942 if( !documentValidation )
2943 return tl::unexpected( documentValidation.error() );
2944
2945 JOB_PCB_RENDER job;
2947 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2948
2951 job.m_bgStyle = FromProtoEnum<JOB_PCB_RENDER::BG_STYLE>( aCtx.Request.background_style() );
2952
2953 job.m_width = aCtx.Request.width();
2954 job.m_height = aCtx.Request.height();
2955 job.m_appearancePreset = aCtx.Request.appearance_preset();
2956 job.m_useBoardStackupColors = aCtx.Request.use_board_stackup_colors();
2957
2959
2960 job.m_zoom = aCtx.Request.zoom();
2961 job.m_perspective = aCtx.Request.perspective();
2962
2963 job.m_rotation = UnpackVector3D( aCtx.Request.rotation() );
2964 job.m_pan = UnpackVector3D( aCtx.Request.pan() );
2965 job.m_pivot = UnpackVector3D( aCtx.Request.pivot() );
2966
2967 job.m_proceduralTextures = aCtx.Request.procedural_textures();
2968 job.m_floor = aCtx.Request.floor();
2969 job.m_antiAlias = aCtx.Request.anti_alias();
2970 job.m_postProcess = aCtx.Request.post_process();
2971
2972 job.m_lightTopIntensity = UnpackVector3D( aCtx.Request.light_top_intensity() );
2973 job.m_lightBottomIntensity = UnpackVector3D( aCtx.Request.light_bottom_intensity() );
2974 job.m_lightCameraIntensity = UnpackVector3D( aCtx.Request.light_camera_intensity() );
2975 job.m_lightSideIntensity = UnpackVector3D( aCtx.Request.light_side_intensity() );
2976 job.m_lightSideElevation = aCtx.Request.light_side_elevation();
2977
2978 return ExecuteBoardJob( context(), job );
2979}
2980
2981
2984{
2985 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2986 return tl::unexpected( *busy );
2987
2988 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2989
2990 if( !documentValidation )
2991 return tl::unexpected( documentValidation.error() );
2992
2995 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2996
2997 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2998 return tl::unexpected( *err );
2999
3000 job.m_fitPageToBoard = aCtx.Request.fit_page_to_board();
3001 job.m_precision = aCtx.Request.precision();
3002
3003 if( std::optional<ApiResponseStatus> paginationError =
3005 "RunBoardJobExportSvg" ) )
3006 {
3007 return tl::unexpected( *paginationError );
3008 }
3009
3011
3012 return ExecuteBoardJob( context(), job );
3013}
3014
3015
3018{
3019 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3020 return tl::unexpected( *busy );
3021
3022 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3023
3024 if( !documentValidation )
3025 return tl::unexpected( documentValidation.error() );
3026
3029 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3030
3031 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
3032 return tl::unexpected( *err );
3033
3034 job.m_plotGraphicItemsUsingContours = aCtx.Request.plot_graphic_items_using_contours();
3035 job.m_polygonMode = aCtx.Request.polygon_mode();
3036
3037 if( std::optional<ApiResponseStatus> unitError =
3038 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDxf" ) )
3039 {
3040 return tl::unexpected( *unitError );
3041 }
3042
3044
3045 if( std::optional<ApiResponseStatus> paginationError =
3047 "RunBoardJobExportDxf" ) )
3048 {
3049 return tl::unexpected( *paginationError );
3050 }
3051
3053
3054 return ExecuteBoardJob( context(), job );
3055}
3056
3057
3060{
3061 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3062 return tl::unexpected( *busy );
3063
3064 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3065
3066 if( !documentValidation )
3067 return tl::unexpected( documentValidation.error() );
3068
3071 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3072
3073 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
3074 return tl::unexpected( *err );
3075
3076 job.m_pdfFrontFPPropertyPopups = aCtx.Request.front_footprint_property_popups();
3077 job.m_pdfBackFPPropertyPopups = aCtx.Request.back_footprint_property_popups();
3078 job.m_pdfMetadata = aCtx.Request.include_metadata();
3079 job.m_pdfSingle = aCtx.Request.single_document();
3080 job.m_pdfBackgroundColor = wxString::FromUTF8( aCtx.Request.background_color() );
3081
3083
3084 return ExecuteBoardJob( context(), job );
3085}
3086
3087
3090{
3091 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3092 return tl::unexpected( *busy );
3093
3094 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3095
3096 if( !documentValidation )
3097 return tl::unexpected( documentValidation.error() );
3098
3101 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3102
3103 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
3104 return tl::unexpected( *err );
3105
3106 if( std::optional<ApiResponseStatus> paginationError =
3108 "RunBoardJobExportPs" ) )
3109 {
3110 return tl::unexpected( *paginationError );
3111 }
3112
3114
3115 job.m_trackWidthCorrection = aCtx.Request.track_width_correction();
3116 job.m_XScaleAdjust = aCtx.Request.x_scale_adjust();
3117 job.m_YScaleAdjust = aCtx.Request.y_scale_adjust();
3118 job.m_forceA4 = aCtx.Request.force_a4();
3119 job.m_useGlobalSettings = aCtx.Request.use_global_settings();
3120
3121 return ExecuteBoardJob( context(), job );
3122}
3123
3124
3127{
3128 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3129 return tl::unexpected( *busy );
3130
3131 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3132
3133 if( !documentValidation )
3134 return tl::unexpected( documentValidation.error() );
3135
3136 if( aCtx.Request.layers().empty() )
3137 {
3138 ApiResponseStatus e;
3139 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3140 e.set_error_message( "RunBoardJobExportGerbers requires at least one layer" );
3141 return tl::unexpected( e );
3142 }
3143
3146 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3147
3148 for( int layer : aCtx.Request.layers() )
3149 {
3150 PCB_LAYER_ID layerId =
3152 static_cast<board::types::BoardLayer>( layer ) );
3153
3154 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
3155 {
3156 ApiResponseStatus e;
3157 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3158 e.set_error_message( "RunBoardJobExportGerbers contains an invalid layer" );
3159 return tl::unexpected( e );
3160 }
3161
3162 job.m_plotLayerSequence.push_back( layerId );
3163 }
3164
3165 return ExecuteBoardJob( context(), job );
3166}
3167
3168
3171{
3172 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3173 return tl::unexpected( *busy );
3174
3175 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3176
3177 if( !documentValidation )
3178 return tl::unexpected( documentValidation.error() );
3179
3182 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3183
3185
3186 if( std::optional<ApiResponseStatus> unitError =
3187 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDrill" ) )
3188 {
3189 return tl::unexpected( *unitError );
3190 }
3191
3195
3196 if( aCtx.Request.has_excellon() )
3197 {
3198 const ExcellonFormatOptions& excellonOptions = aCtx.Request.excellon();
3199
3200 if( excellonOptions.has_mirror_y() )
3201 job.m_excellonMirrorY = excellonOptions.mirror_y();
3202
3203 if( excellonOptions.has_minimal_header() )
3204 job.m_excellonMinimalHeader = excellonOptions.minimal_header();
3205
3206 if( excellonOptions.has_combine_pth_npth() )
3207 job.m_excellonCombinePTHNPTH = excellonOptions.combine_pth_npth();
3208
3209 if( excellonOptions.has_route_oval_holes() )
3210 job.m_excellonOvalDrillRoute = excellonOptions.route_oval_holes();
3211 }
3212
3213 if( aCtx.Request.map_format() != DrillMapFormat::DMF_UNKNOWN )
3214 {
3215 job.m_generateMap = true;
3217 }
3218
3219 job.m_gerberPrecision = aCtx.Request.gerber_precision() == DrillGerberPrecision::DGP_4_5 ? 5 : 6;
3220
3221 if( aCtx.Request.has_gerber_generate_tenting() )
3222 job.m_generateTenting = aCtx.Request.gerber_generate_tenting();
3223
3224 if( aCtx.Request.report_format() != DrillReportFormat::DRF_UNKNOWN )
3225 {
3226 job.m_generateReport = true;
3227
3228 if( aCtx.Request.has_report_filename() )
3229 job.m_reportPath = wxString::FromUTF8( aCtx.Request.report_filename() );
3230 }
3231
3232 return ExecuteBoardJob( context(), job );
3233}
3234
3235
3238{
3239 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3240 return tl::unexpected( *busy );
3241
3242 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3243
3244 if( !documentValidation )
3245 return tl::unexpected( documentValidation.error() );
3246
3249 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3250
3251 if( aCtx.Request.has_use_drill_place_file_origin() )
3252 job.m_useDrillPlaceFileOrigin = aCtx.Request.use_drill_place_file_origin();
3253
3254 job.m_smdOnly = aCtx.Request.smd_only();
3255 job.m_excludeFootprintsWithTh = aCtx.Request.exclude_footprints_with_th();
3256 job.m_excludeDNP = aCtx.Request.exclude_dnp();
3257 job.m_excludeBOM = aCtx.Request.exclude_from_bom();
3258 job.m_negateBottomX = aCtx.Request.negate_bottom_x();
3259 job.m_singleFile = aCtx.Request.single_file();
3260 job.m_nakedFilename = aCtx.Request.naked_filename();
3261 if( aCtx.Request.has_include_board_edge_for_gerber() )
3262 job.m_gerberBoardEdge = aCtx.Request.include_board_edge_for_gerber();
3263
3264 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
3265
3267
3268 if( std::optional<ApiResponseStatus> unitError =
3269 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportPosition" ) )
3270 {
3271 return tl::unexpected( *unitError );
3272 }
3273
3276
3277 return ExecuteBoardJob( context(), job );
3278}
3279
3280
3283{
3284 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3285 return tl::unexpected( *busy );
3286
3287 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3288
3289 if( !documentValidation )
3290 return tl::unexpected( documentValidation.error() );
3291
3294 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3295
3296 job.m_flipBottomPads = aCtx.Request.flip_bottom_pads();
3297 job.m_useIndividualShapes = aCtx.Request.use_individual_shapes();
3298 job.m_storeOriginCoords = aCtx.Request.store_origin_coords();
3299 job.m_useDrillOrigin = aCtx.Request.use_drill_origin();
3300 job.m_useUniquePins = aCtx.Request.use_unique_pins();
3301
3302 return ExecuteBoardJob( context(), job );
3303}
3304
3305
3308{
3309 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3310 return tl::unexpected( *busy );
3311
3312 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3313
3314 if( !documentValidation )
3315 return tl::unexpected( documentValidation.error() );
3316
3319 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3320
3321 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
3322 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
3323 if( aCtx.Request.has_precision() )
3324 job.m_precision = aCtx.Request.precision();
3325
3326 job.m_compress = aCtx.Request.compress();
3327 job.m_colInternalId = wxString::FromUTF8( aCtx.Request.internal_id_column() );
3328 job.m_colMfgPn = wxString::FromUTF8( aCtx.Request.manufacturer_part_number_column() );
3329 job.m_colMfg = wxString::FromUTF8( aCtx.Request.manufacturer_column() );
3330 job.m_colDistPn = wxString::FromUTF8( aCtx.Request.distributor_part_number_column() );
3331 job.m_colDist = wxString::FromUTF8( aCtx.Request.distributor_column() );
3332 job.m_bomRev = wxString::FromUTF8( aCtx.Request.bom_revision() );
3333
3334 if( std::optional<ApiResponseStatus> unitError =
3335 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportIpc2581" ) )
3336 {
3337 return tl::unexpected( *unitError );
3338 }
3339
3342
3343 return ExecuteBoardJob( context(), job );
3344}
3345
3346
3349{
3350 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3351 return tl::unexpected( *busy );
3352
3353 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3354
3355 if( !documentValidation )
3356 return tl::unexpected( documentValidation.error() );
3357
3360 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3361
3362 return ExecuteBoardJob( context(), job );
3363}
3364
3365
3368{
3369 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3370 return tl::unexpected( *busy );
3371
3372 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3373
3374 if( !documentValidation )
3375 return tl::unexpected( documentValidation.error() );
3376
3379 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3380
3381 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
3382 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
3383 if( aCtx.Request.has_precision() )
3384 job.m_precision = aCtx.Request.precision();
3385
3386 if( std::optional<ApiResponseStatus> unitError =
3387 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportODB" ) )
3388 {
3389 return tl::unexpected( *unitError );
3390 }
3391
3394
3395 return ExecuteBoardJob( context(), job );
3396}
3397
3398
3401{
3402 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3403 return tl::unexpected( *busy );
3404
3405 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
3406
3407 if( !documentValidation )
3408 return tl::unexpected( documentValidation.error() );
3409
3412 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
3413
3415
3416 if( std::optional<ApiResponseStatus> unitError =
3417 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportStats" ) )
3418 {
3419 return tl::unexpected( *unitError );
3420 }
3421
3423
3424 job.m_excludeFootprintsWithoutPads = aCtx.Request.exclude_footprints_without_pads();
3425 job.m_subtractHolesFromBoardArea = aCtx.Request.subtract_holes_from_board_area();
3426 job.m_subtractHolesFromCopperAreas = aCtx.Request.subtract_holes_from_copper_areas();
3427
3428 return ExecuteBoardJob( context(), job );
3429}
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:41
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
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< std::optional< KIID > > validateItemHeaderDocument(const kiapi::common::types::ItemHeader &aHeader)
If the header is valid, returns the item container.
API_HANDLER_EDITOR(EDA_BASE_FRAME *aFrame=nullptr)
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)
HANDLER_RESULT< Empty > handleInteractiveMoveItems(const HANDLER_CONTEXT< InteractiveMoveItems > &aCtx)
bool validateDocumentInternal(const DocumentSpecifier &aDocument) const override
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)
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< types::TitleBlockInfo > handleGetTitleBlockInfo(const HANDLER_CONTEXT< commands::GetTitleBlockInfo > &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< Empty > handleSetTitleBlockInfo(const HANDLER_CONTEXT< commands::SetTitleBlockInfo > &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)
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsByNet(const HANDLER_CONTEXT< GetItemsByNet > &aCtx)
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)
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)
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)
std::vector< KICAD_T > parseRequestedItemTypes(const google::protobuf::RepeatedField< int > &aTypes)
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
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:3110
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:1008
void SetDesignSettings(const BOARD_DESIGN_SETTINGS &aSettings)
Definition board.cpp:1097
TITLE_BLOCK & GetTitleBlock()
Definition board.h:813
const wxString & GetFileName() const
Definition board.h:360
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1091
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:976
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1779
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:376
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:82
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:748
@ 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
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 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:401
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.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:41
const wxString & GetCompany() const
Definition title_block.h:96
void SetRevision(const wxString &aRevision)
Definition title_block.h:81
void SetComment(int aIdx, const wxString &aComment)
const wxString & GetRevision() const
Definition title_block.h:86
void SetTitle(const wxString &aTitle)
Definition title_block.h:58
const wxString & GetDate() const
Definition title_block.h:76
const wxString & GetComment(int aIdx) const
void SetCompany(const wxString &aCompany)
Definition title_block.h:91
const wxString & GetTitle() const
Definition title_block.h:63
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:71
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:191
const wxString & GetMessages() const
Definition reporter.cpp:78
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:62
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 void PackBox2(types::Box2 &aOutput, const BOX2I &aInput)
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition api_utils.cpp:33
KICOMMON_API VECTOR3D UnpackVector3D(const types::Vector3D &aInput)
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput)
Definition api_utils.cpp:86
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition api_utils.cpp:79
KICOMMON_API void PackPolyLine(types::PolyLine &aOutput, const SHAPE_LINE_CHAIN &aSlc)
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
@ TYPE_NOT_INIT
Definition typeinfo.h:78
@ 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