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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <magic_enum.hpp>
22#include <memory>
23#include <properties/property.h>
24
25#include <common.h>
26#include <fmt.h>
27#include <api/api_handler_pcb.h>
28#include <api/api_pcb_utils.h>
29#include <api/api_enums.h>
30#include <api/api_utils.h>
31#include <api/common/commands/library_commands.pb.h>
33#include <wx/log.h>
34#include <base_screen.h>
35#include <board_commit.h>
38#include <core/kicad_algo.h>
39#include <footprint.h>
41#include <kicad_clipboard.h>
42#include <netinfo.h>
43#include <pad.h>
44#include <pcb_draw_panel_gal.h>
45#include <pcb_edit_frame.h>
46#include <pcb_group.h>
47#include <pcb_reference_image.h>
48#include <pcb_shape.h>
49#include <pcb_text.h>
50#include <pcb_textbox.h>
51#include <pcb_track.h>
52#include <pcbnew_id.h>
53#include <pcb_marker.h>
54#include <pcb_point.h>
55#include <kiway.h>
56#include <drc/drc_item.h>
68#include <project_pcb.h>
74#include <pcb_plot_params.h>
76#include <jobs/job_pcb_render.h>
77#include <layer_ids.h>
80#include <project.h>
81#include <tool/actions.h>
82#include <string_utils.h>
83#include <tool/tool_manager.h>
84#include <tools/pcb_actions.h>
87#include <zone.h>
88#include <zone_filler.h>
89
90#include <api/common/types/base_types.pb.h>
91#include <api/board/board_rules.pb.h>
93#include <google/protobuf/util/json_util.h>
95#include <drc/drc_rule_parser.h>
99#include <trace_helpers.h>
100#include <wx/ffile.h>
101
102using namespace kiapi::common::commands;
103using namespace kiapi::board::commands;
104using types::CommandStatus;
105using types::DocumentType;
106using types::ItemRequestStatus;
107
108
113
114
115API_HANDLER_PCB::API_HANDLER_PCB( std::shared_ptr<PCB_CONTEXT> aContext, PCB_EDIT_FRAME* aFrame ) :
116 API_HANDLER_BOARD( std::move( aContext ), aFrame )
117{
123
125
138
147
156
166
197
200
204
207}
208
209
211{
212 return static_cast<PCB_EDIT_FRAME*>( m_frame );
213}
214
215
218{
219 if( aCtx.Request.type() != DocumentType::DOCTYPE_PCB )
220 {
221 ApiResponseStatus e;
222 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
223 e.set_status( ApiStatusCode::AS_UNHANDLED );
224 return tl::unexpected( e );
225 }
226
227 GetOpenDocumentsResponse response;
228 common::types::DocumentSpecifier doc;
229
230 wxFileName fn( pcbContext()->GetCurrentFileName() );
231
232 doc.set_type( DocumentType::DOCTYPE_PCB );
233 doc.set_board_filename( fn.GetFullName() );
234
235 doc.mutable_project()->set_name( project().GetProjectName().ToStdString() );
236 doc.mutable_project()->set_path( project().GetProjectDirectory().ToStdString() );
237
238 response.mutable_documents()->Add( std::move( doc ) );
239 return response;
240}
241
242
245{
246 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
247 return tl::unexpected( *busy );
248
249 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
250
251 if( !documentValidation )
252 return tl::unexpected( documentValidation.error() );
253
255 return Empty();
256}
257
258
261{
262 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
263 return tl::unexpected( *busy );
264
265 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
266
267 if( !documentValidation )
268 return tl::unexpected( documentValidation.error() );
269
270 wxFileName boardPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.path() ) ) );
271
272 if( !boardPath.IsOk() || !boardPath.IsDirWritable() )
273 {
274 ApiResponseStatus e;
275 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
276 e.set_error_message( fmt::format( "save path '{}' could not be opened",
277 boardPath.GetFullPath().ToStdString() ) );
278 return tl::unexpected( e );
279 }
280
281 if( boardPath.FileExists()
282 && ( !boardPath.IsFileWritable() || !aCtx.Request.options().overwrite() ) )
283 {
284 ApiResponseStatus e;
285 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
286 e.set_error_message( fmt::format( "save path '{}' exists and cannot be overwritten",
287 boardPath.GetFullPath().ToStdString() ) );
288 return tl::unexpected( e );
289 }
290
291 if( boardPath.GetExt() != FILEEXT::KiCadPcbFileExtension )
292 {
293 ApiResponseStatus e;
294 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
295 e.set_error_message( fmt::format( "save path '{}' must have a kicad_pcb extension",
296 boardPath.GetFullPath().ToStdString() ) );
297 return tl::unexpected( e );
298 }
299
300 BOARD* board = this->board();
301
302 if( board->GetFileName().Matches( boardPath.GetFullPath() ) )
303 {
305 return Empty();
306 }
307
308 bool includeProject = true;
309
310 if( aCtx.Request.has_options() )
311 includeProject = aCtx.Request.options().include_project();
312
313 pcbContext()->SavePcbCopy( boardPath.GetFullPath(), includeProject, /* aHeadless = */ true );
314
315 return Empty();
316}
317
318
321{
322 // Validate first so a request meant for another editor's document is not captured here
323 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
324
325 if( !documentValidation )
326 return tl::unexpected( documentValidation.error() );
327
328 // Reloading frees every item, so refuse while any client transaction is open; the staged
329 // commit would otherwise dangle
330 if( !m_commits.empty() )
331 {
332 ApiResponseStatus e;
333 e.set_status( ApiStatusCode::AS_BUSY );
334 e.set_error_message( "cannot revert while a commit is open" );
335 return tl::unexpected( e );
336 }
337
338 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
339 return tl::unexpected( *busy );
340
341 if( !pcbContext()->RevertToSaved() )
342 {
343 ApiResponseStatus e;
344 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
345 e.set_error_message( "could not revert: there is no saved file on disk to revert to" );
346 return tl::unexpected( e );
347 }
348
349 return Empty();
350}
351
352
353tl::expected<bool, ApiResponseStatus> API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
354{
355 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
356 {
357 ApiResponseStatus e;
358 e.set_status( ApiStatusCode::AS_UNHANDLED );
359 return tl::unexpected( e );
360 }
361
362 wxFileName fn( pcbContext()->GetCurrentFileName() );
363
364 if( aDocument.board_filename().compare( fn.GetFullName() ) != 0 )
365 {
366 ApiResponseStatus e;
367 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
368 e.set_error_message( fmt::format( "the requested document {} is not open",
369 aDocument.board_filename() ) );
370 return tl::unexpected( e );
371 }
372
373 return true;
374}
375
376
377// Board types that are directly retrievable by GetItems
397
398
400{
401 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
402 return tl::unexpected( *busy );
403
404 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
405 {
406 ApiResponseStatus e;
407 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
408 e.set_status( ApiStatusCode::AS_UNHANDLED );
409 return tl::unexpected( e );
410 }
411
412 GetItemsResponse response;
413
414 BOARD* board = this->board();
415 std::vector<BOARD_ITEM*> items;
416 std::set<KICAD_T> typesRequested, typesInserted;
417 bool handledAnything = false;
418
419 std::vector<KICAD_T> requestedTypes = parseRequestedItemTypes( aCtx.Request.types() );
420
421 if( aCtx.Request.types().empty() )
422 requestedTypes.assign( s_allowedBoardTypes.begin(), s_allowedBoardTypes.end() );
423
424 for( KICAD_T type : requestedTypes )
425 {
426 typesRequested.emplace( type );
427
428 if( typesInserted.count( type ) )
429 continue;
430
431 switch( type )
432 {
433 case PCB_TRACE_T:
434 case PCB_ARC_T:
435 case PCB_VIA_T:
436 handledAnything = true;
437 std::copy( board->Tracks().begin(), board->Tracks().end(),
438 std::back_inserter( items ) );
439 typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
440 break;
441
442 case PCB_PAD_T:
443 {
444 handledAnything = true;
445
446 for( FOOTPRINT* fp : board->Footprints() )
447 {
448 std::copy( fp->Pads().begin(), fp->Pads().end(),
449 std::back_inserter( items ) );
450 }
451
452 typesInserted.insert( PCB_PAD_T );
453 break;
454 }
455
456 case PCB_FOOTPRINT_T:
457 {
458 handledAnything = true;
459
460 std::copy( board->Footprints().begin(), board->Footprints().end(),
461 std::back_inserter( items ) );
462
463 typesInserted.insert( PCB_FOOTPRINT_T );
464 break;
465 }
466
467 case PCB_SHAPE_T:
468 case PCB_TABLE_T:
469 case PCB_TEXT_T:
470 case PCB_TEXTBOX_T:
471 case PCB_BARCODE_T:
473 case PCB_GRID_ITEM_T:
474 {
475 handledAnything = true;
476 bool inserted = false;
477
478 for( BOARD_ITEM* item : board->Drawings() )
479 {
480 if( item->Type() == type )
481 {
482 items.emplace_back( item );
483 inserted = true;
484 }
485 }
486
487 if( inserted )
488 typesInserted.insert( type );
489
490 break;
491 }
492
493 case PCB_DIMENSION_T:
494 {
495 handledAnything = true;
496 bool inserted = false;
497
498 for( BOARD_ITEM* item : board->Drawings() )
499 {
500 switch (item->Type()) {
502 case PCB_DIM_CENTER_T:
503 case PCB_DIM_RADIAL_T:
505 case PCB_DIM_LEADER_T:
506 items.emplace_back( item );
507 inserted = true;
508 break;
509 default:
510 break;
511 }
512 }
513 // we have to add the dimension subtypes to the requested to get them out
515
516 if( inserted )
518
519 break;
520 }
521
522 case PCB_ZONE_T:
523 {
524 handledAnything = true;
525
526 std::copy( board->Zones().begin(), board->Zones().end(),
527 std::back_inserter( items ) );
528
529 typesInserted.insert( PCB_ZONE_T );
530 break;
531 }
532
533 case PCB_GROUP_T:
534 {
535 handledAnything = true;
536
537 std::copy( board->Groups().begin(), board->Groups().end(),
538 std::back_inserter( items ) );
539
540 typesInserted.insert( PCB_GROUP_T );
541 break;
542 }
543
544 case PCB_POINT_T:
545 {
546 handledAnything = true;
547 std::copy( board->Points().begin(), board->Points().end(), std::back_inserter( items ) );
548 typesInserted.insert( PCB_POINT_T );
549 break;
550 }
551
552 case PCB_CONSTRAINT_T:
553 {
554 handledAnything = true;
555
556 std::copy( board->Constraints().begin(), board->Constraints().end(),
557 std::back_inserter( items ) );
558
559 typesInserted.insert( PCB_CONSTRAINT_T );
560 break;
561 }
562
563 default:
564 break;
565 }
566 }
567
568 if( !handledAnything )
569 {
570 ApiResponseStatus e;
571 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
572 e.set_error_message( "none of the requested types are valid for a Board object" );
573 return tl::unexpected( e );
574 }
575
576 for( const BOARD_ITEM* item : items )
577 {
578 if( !typesRequested.count( item->Type() ) )
579 continue;
580
581 google::protobuf::Any itemBuf;
582 item->Serialize( itemBuf );
583 response.mutable_items()->Add( std::move( itemBuf ) );
584 }
585
586 response.set_status( ItemRequestStatus::IRS_OK );
587 return response;
588}
589
590
593{
594 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
595
596 if( !documentValidation )
597 return tl::unexpected( documentValidation.error() );
598
599 if( aCtx.Request.copper_layer_count() % 2 != 0 )
600 {
601 ApiResponseStatus e;
602 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
603 e.set_error_message( "copper_layer_count must be an even number" );
604 return tl::unexpected( e );
605 }
606
607 if( aCtx.Request.copper_layer_count() > MAX_CU_LAYERS )
608 {
609 ApiResponseStatus e;
610 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
611 e.set_error_message( fmt::format( "copper_layer_count must be below %d", MAX_CU_LAYERS ) );
612 return tl::unexpected( e );
613 }
614
615 int copperLayerCount = static_cast<int>( aCtx.Request.copper_layer_count() );
616 LSET enabled = board::UnpackLayerSet( aCtx.Request.layers() );
617
618 // Sanitize the input
619 enabled |= LSET( { Edge_Cuts, Margin, F_CrtYd, B_CrtYd } );
620 enabled &= ~LSET::AllCuMask();
621 enabled |= LSET::AllCuMask( copperLayerCount );
622
623 BOARD* board = this->board();
624
625 LSET previousEnabled = board->GetEnabledLayers();
626 LSET changedLayers = enabled ^ previousEnabled;
627
628 board->SetEnabledLayers( enabled );
629 board->SetVisibleLayers( board->GetVisibleLayers() | changedLayers );
630
631 LSEQ removedLayers;
632
633 for( PCB_LAYER_ID layer_id : previousEnabled )
634 {
635 if( !enabled[layer_id] && board->HasItemsOnLayer( layer_id ) )
636 removedLayers.push_back( layer_id );
637 }
638
639 bool modified = false;
640
641 if( !removedLayers.empty() )
642 {
644
645 for( PCB_LAYER_ID layer_id : removedLayers )
646 modified |= board->RemoveAllItemsOnLayer( layer_id );
647 }
648
649 if( frame() )
650 {
651 if( enabled != previousEnabled )
653
654 if( modified )
655 frame()->OnModify();
656 }
657
658 BoardEnabledLayersResponse response;
659
660 response.set_copper_layer_count( copperLayerCount );
661 board::PackLayerSet( *response.mutable_layers(), enabled );
662
663 return response;
664}
665
666
669{
670 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() ); !documentValidation )
671 return tl::unexpected( documentValidation.error() );
672
673 common::types::EmbeddedFiles response;
674 board::PackEmbeddedFiles( response, *board()->GetEmbeddedFiles() );
675 return response;
676}
677
678
679HANDLER_RESULT<Empty> unpackEmbeddedFiles( EMBEDDED_FILES& aOutput, const common::types::EmbeddedFiles& aProto )
680{
681 if( !board::UnpackEmbeddedFiles( aOutput, aProto ) )
682 {
683 ApiResponseStatus e;
684 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
685 e.set_error_message( "embedded file validation failed" );
686 return tl::unexpected( e );
687 }
688
689 return Empty();
690}
691
692
694{
695 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
696 return tl::unexpected( *busy );
697
698 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() ); !documentValidation )
699 return tl::unexpected( documentValidation.error() );
700
701 EMBEDDED_FILES files;
703
704 if( !result.has_value() )
705 return result;
706
707 EMBEDDED_FILES* boardFiles = board()->GetEmbeddedFiles();
708
709 for( const std::shared_ptr<EMBEDDED_FILES::EMBEDDED_FILE>& file : files.EmbeddedFileMap() | std::views::values )
710 {
711 auto copy = std::make_shared<EMBEDDED_FILES::EMBEDDED_FILE>( *file );
712 boardFiles->AddFile( copy );
713 }
714
715 onModified();
716 return result;
717}
718
719
721{
722 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
723 return tl::unexpected( *busy );
724
725 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() ); !documentValidation )
726 return tl::unexpected( documentValidation.error() );
727
728 HANDLER_RESULT<Empty> result = unpackEmbeddedFiles( *board()->GetEmbeddedFiles(),
729 aCtx.Request.files() );
730
731 if( !result.has_value() )
732 return result;
733
734 onModified();
735 return result;
736}
737
738
741{
742 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
743
744 if( !documentValidation )
745 return tl::unexpected( documentValidation.error() );
746
748 BoardDesignRulesResponse response;
749 kiapi::board::BoardDesignRules* rules = response.mutable_rules();
750
751 kiapi::board::MinimumConstraints* constraints = rules->mutable_constraints();
752
753 constraints->mutable_min_clearance()->set_value_nm( bds.m_MinClearance );
754 constraints->mutable_min_groove_width()->set_value_nm( bds.m_MinGrooveWidth );
755 constraints->mutable_min_connection_width()->set_value_nm( bds.m_MinConn );
756 constraints->mutable_min_track_width()->set_value_nm( bds.m_TrackMinWidth );
757 constraints->mutable_min_via_annular_width()->set_value_nm( bds.m_ViasMinAnnularWidth );
758 constraints->mutable_min_via_size()->set_value_nm( bds.m_ViasMinSize );
759 constraints->mutable_min_through_drill()->set_value_nm( bds.m_MinThroughDrill );
760 constraints->mutable_min_microvia_size()->set_value_nm( bds.m_MicroViasMinSize );
761 constraints->mutable_min_microvia_drill()->set_value_nm( bds.m_MicroViasMinDrill );
762 constraints->mutable_copper_edge_clearance()->set_value_nm( bds.m_CopperEdgeClearance );
763 constraints->mutable_hole_clearance()->set_value_nm( bds.m_HoleClearance );
764 constraints->mutable_hole_to_hole_min()->set_value_nm( bds.m_HoleToHoleMin );
765 constraints->mutable_silk_clearance()->set_value_nm( bds.m_SilkClearance );
766 constraints->set_min_resolved_spokes( bds.m_MinResolvedSpokes );
767 constraints->mutable_min_silk_text_height()->set_value_nm( bds.m_MinSilkTextHeight );
768 constraints->mutable_min_silk_text_thickness()->set_value_nm( bds.m_MinSilkTextThickness );
769
770 kiapi::board::PredefinedSizes* sizes = rules->mutable_predefined_sizes();
771
772 for( size_t ii = 1; ii < bds.m_TrackWidthList.size(); ++ii )
773 sizes->add_tracks()->mutable_width()->set_value_nm( bds.m_TrackWidthList[ii] );
774
775 for( size_t ii = 1; ii < bds.m_ViasDimensionsList.size(); ++ii )
776 {
777 kiapi::board::PresetViaDimension* via = sizes->add_vias();
778 via->mutable_diameter()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Diameter );
779 via->mutable_drill()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Drill );
780 }
781
782 for( size_t ii = 1; ii < bds.m_DiffPairDimensionsList.size(); ++ii )
783 {
784 kiapi::board::PresetDiffPairDimension* pair = sizes->add_diff_pairs();
785 pair->mutable_width()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Width );
786 pair->mutable_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Gap );
787 pair->mutable_via_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_ViaGap );
788 }
789
790 kiapi::board::SolderMaskPasteDefaults* maskPaste = rules->mutable_solder_mask_paste();
791
792 maskPaste->mutable_mask_expansion()->set_value_nm( bds.m_SolderMaskExpansion );
793 maskPaste->mutable_mask_min_width()->set_value_nm( bds.m_SolderMaskMinWidth );
794 maskPaste->mutable_mask_to_copper_clearance()->set_value_nm( bds.m_SolderMaskToCopperClearance );
795 maskPaste->mutable_paste_margin()->set_value_nm( bds.m_SolderPasteMargin );
796 maskPaste->set_paste_margin_ratio( bds.m_SolderPasteMarginRatio );
797 maskPaste->set_allow_soldermask_bridges_in_footprints( bds.m_AllowSoldermaskBridgesInFPs );
798
799 kiapi::board::TeardropDefaults* teardrops = rules->mutable_teardrops();
800
801 teardrops->set_target_vias( bds.m_TeardropParamsList.m_TargetVias );
802 teardrops->set_target_pth_pads( bds.m_TeardropParamsList.m_TargetPTHPads );
803 teardrops->set_target_smd_pads( bds.m_TeardropParamsList.m_TargetSMDPads );
804 teardrops->set_target_track_to_track( bds.m_TeardropParamsList.m_TargetTrack2Track );
805 teardrops->set_use_round_shapes_only( bds.m_TeardropParamsList.m_UseRoundShapesOnly );
806
808
809 for( int target = TARGET_ROUND; target <= TARGET_TRACK; ++target )
810 {
811 const TEARDROP_PARAMETERS* params = tdList.GetParameters( static_cast<TARGET_TD>( target ) );
812 kiapi::board::TeardropTargetEntry* entry = teardrops->add_target_params();
813
815 static_cast<TARGET_TD>( target ) ) );
816 entry->mutable_params()->set_enabled( params->m_Enabled );
817 entry->mutable_params()->mutable_max_length()->set_value_nm( params->m_TdMaxLen );
818 entry->mutable_params()->mutable_max_width()->set_value_nm( params->m_TdMaxWidth );
819 entry->mutable_params()->set_best_length_ratio( params->m_BestLengthRatio );
820 entry->mutable_params()->set_best_width_ratio( params->m_BestWidthRatio );
821 entry->mutable_params()->set_width_to_size_filter_ratio( params->m_WidthtoSizeFilterRatio );
822 entry->mutable_params()->set_curved_edges( params->m_CurvedEdges );
823 entry->mutable_params()->set_allow_two_tracks( params->m_AllowUseTwoTracks );
824 entry->mutable_params()->set_on_pads_in_zones( params->m_TdOnPadsInZones );
825 }
826
827 kiapi::board::ViaProtectionDefaults* viaProtection = rules->mutable_via_protection();
828
829 viaProtection->set_tent_front( bds.m_TentViasFront );
830 viaProtection->set_tent_back( bds.m_TentViasBack );
831 viaProtection->set_cover_front( bds.m_CoverViasFront );
832 viaProtection->set_cover_back( bds.m_CoverViasBack );
833 viaProtection->set_plug_front( bds.m_PlugViasFront );
834 viaProtection->set_plug_back( bds.m_PlugViasBack );
835 viaProtection->set_cap( bds.m_CapVias );
836 viaProtection->set_fill( bds.m_FillVias );
837
838 for( const auto& [errorCode, severity] : bds.m_DRCSeverities )
839 {
840 board::DrcSeveritySetting* setting = rules->add_severities();
841 setting->set_rule_type(
843 setting->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( severity ) );
844 }
845
846 for( const DRC_EXCLUSION& exclusion : bds.m_DrcExclusions )
847 rules->add_exclusions()->CopyFrom( exclusion.ToProto() );
848
849 response.set_custom_rules_status( CRS_NONE );
850
851 wxString rulesPath = board()->GetDesignRulesPath();
852
853 if( !rulesPath.IsEmpty() && wxFileName::IsFileReadable( rulesPath ) )
854 {
855 wxFFile file( rulesPath, "r" );
856 wxString content;
857 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
858
859 if( !file.IsOpened() )
860 {
861 response.set_custom_rules_status( CRS_INVALID );
862 return response;
863 }
864
865 file.ReadAll( &content );
866 file.Close();
867
868 try
869 {
870 DRC_RULES_PARSER parser( content, "File" );
871 parser.Parse( parsedRules, nullptr );
872 response.set_custom_rules_status( CRS_VALID );
873 }
874 catch( const IO_ERROR& )
875 {
876 response.set_custom_rules_status( CRS_INVALID );
877 }
878 }
879
880 return response;
881}
882
883
886{
887 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
888
889 if( !documentValidation )
890 return tl::unexpected( documentValidation.error() );
891
892 BOARD_DESIGN_SETTINGS newSettings( board()->GetDesignSettings() );
893 const kiapi::board::BoardDesignRules& rules = aCtx.Request.rules();
894
895 if( rules.has_constraints() )
896 {
897 const kiapi::board::MinimumConstraints& constraints = rules.constraints();
898
899 newSettings.m_MinClearance = constraints.min_clearance().value_nm();
900 newSettings.m_MinGrooveWidth = constraints.min_groove_width().value_nm();
901 newSettings.m_MinConn = constraints.min_connection_width().value_nm();
902 newSettings.m_TrackMinWidth = constraints.min_track_width().value_nm();
903 newSettings.m_ViasMinAnnularWidth = constraints.min_via_annular_width().value_nm();
904 newSettings.m_ViasMinSize = constraints.min_via_size().value_nm();
905 newSettings.m_MinThroughDrill = constraints.min_through_drill().value_nm();
906 newSettings.m_MicroViasMinSize = constraints.min_microvia_size().value_nm();
907 newSettings.m_MicroViasMinDrill = constraints.min_microvia_drill().value_nm();
908 newSettings.m_CopperEdgeClearance = constraints.copper_edge_clearance().value_nm();
909 newSettings.m_HoleClearance = constraints.hole_clearance().value_nm();
910 newSettings.m_HoleToHoleMin = constraints.hole_to_hole_min().value_nm();
911 newSettings.m_SilkClearance = constraints.silk_clearance().value_nm();
912 newSettings.m_MinResolvedSpokes = constraints.min_resolved_spokes();
913 newSettings.m_MinSilkTextHeight = constraints.min_silk_text_height().value_nm();
914 newSettings.m_MinSilkTextThickness = constraints.min_silk_text_thickness().value_nm();
915 }
916
917 if( rules.has_predefined_sizes() )
918 {
919 newSettings.m_TrackWidthList.clear();
920 newSettings.m_TrackWidthList.emplace_back( 0 );
921
922 for( const kiapi::board::PresetTrackWidth& track : rules.predefined_sizes().tracks() )
923 newSettings.m_TrackWidthList.emplace_back( track.width().value_nm() );
924
925 newSettings.m_ViasDimensionsList.clear();
926 newSettings.m_ViasDimensionsList.emplace_back( 0, 0 );
927
928 for( const kiapi::board::PresetViaDimension& via : rules.predefined_sizes().vias() )
929 {
930 newSettings.m_ViasDimensionsList.emplace_back( static_cast<int>( via.diameter().value_nm() ),
931 static_cast<int>( via.drill().value_nm() ) );
932 }
933
934 newSettings.m_DiffPairDimensionsList.clear();
935 newSettings.m_DiffPairDimensionsList.emplace_back( 0, 0, 0 );
936
937 for( const kiapi::board::PresetDiffPairDimension& pair : rules.predefined_sizes().diff_pairs() )
938 {
939 newSettings.m_DiffPairDimensionsList.emplace_back(
940 static_cast<int>( pair.width().value_nm() ),
941 static_cast<int>( pair.gap().value_nm() ),
942 static_cast<int>( pair.via_gap().value_nm() ) );
943 }
944 }
945
946 if( rules.has_solder_mask_paste() )
947 {
948 const kiapi::board::SolderMaskPasteDefaults& maskPaste = rules.solder_mask_paste();
949
950 newSettings.m_SolderMaskExpansion = maskPaste.mask_expansion().value_nm();
951 newSettings.m_SolderMaskMinWidth = maskPaste.mask_min_width().value_nm();
952 newSettings.m_SolderMaskToCopperClearance = maskPaste.mask_to_copper_clearance().value_nm();
953 newSettings.m_SolderPasteMargin = maskPaste.paste_margin().value_nm();
954 newSettings.m_SolderPasteMarginRatio = maskPaste.paste_margin_ratio();
956 maskPaste.allow_soldermask_bridges_in_footprints();
957 }
958
959 if( rules.has_teardrops() )
960 {
961 const kiapi::board::TeardropDefaults& teardrops = rules.teardrops();
962
963 newSettings.m_TeardropParamsList.m_TargetVias = teardrops.target_vias();
964 newSettings.m_TeardropParamsList.m_TargetPTHPads = teardrops.target_pth_pads();
965 newSettings.m_TeardropParamsList.m_TargetSMDPads = teardrops.target_smd_pads();
966 newSettings.m_TeardropParamsList.m_TargetTrack2Track = teardrops.target_track_to_track();
967 newSettings.m_TeardropParamsList.m_UseRoundShapesOnly = teardrops.use_round_shapes_only();
968
969 for( const kiapi::board::TeardropTargetEntry& entry : teardrops.target_params() )
970 {
971 if( entry.target() == kiapi::board::TeardropTarget::TDT_UNKNOWN )
972 continue;
973
975 entry.target() );
976
977 TEARDROP_PARAMETERS* params = newSettings.m_TeardropParamsList.GetParameters( target );
978
979 params->m_Enabled = entry.params().enabled();
980 params->m_TdMaxLen = entry.params().max_length().value_nm();
981 params->m_TdMaxWidth = entry.params().max_width().value_nm();
982 params->m_BestLengthRatio = entry.params().best_length_ratio();
983 params->m_BestWidthRatio = entry.params().best_width_ratio();
984 params->m_WidthtoSizeFilterRatio = entry.params().width_to_size_filter_ratio();
985 params->m_CurvedEdges = entry.params().curved_edges();
986 params->m_AllowUseTwoTracks = entry.params().allow_two_tracks();
987 params->m_TdOnPadsInZones = entry.params().on_pads_in_zones();
988 }
989 }
990
991 if( rules.has_via_protection() )
992 {
993 const kiapi::board::ViaProtectionDefaults& viaProtection = rules.via_protection();
994
995 newSettings.m_TentViasFront = viaProtection.tent_front();
996 newSettings.m_TentViasBack = viaProtection.tent_back();
997 newSettings.m_CoverViasFront = viaProtection.cover_front();
998 newSettings.m_CoverViasBack = viaProtection.cover_back();
999 newSettings.m_PlugViasFront = viaProtection.plug_front();
1000 newSettings.m_PlugViasBack = viaProtection.plug_back();
1001 newSettings.m_CapVias = viaProtection.cap();
1002 newSettings.m_FillVias = viaProtection.fill();
1003 }
1004
1005 if( rules.severities_size() > 0 )
1006 {
1007 newSettings.m_DRCSeverities.clear();
1008
1009 for( const kiapi::board::DrcSeveritySetting& severitySetting : rules.severities() )
1010 {
1011 PCB_DRC_CODE ruleType =
1012 FromProtoEnum<PCB_DRC_CODE, kiapi::board::DrcErrorType>( severitySetting.rule_type() );
1013
1014 const std::unordered_set<SEVERITY> permitted( { RPT_SEVERITY_ERROR, RPT_SEVERITY_WARNING, RPT_SEVERITY_IGNORE } );
1015 SEVERITY setting = FromProtoEnum<SEVERITY, kiapi::common::types::RuleSeverity>( severitySetting.severity() );
1016
1017 if( !permitted.contains( setting ) )
1018 {
1019 ApiResponseStatus e;
1020 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1021 e.set_error_message( fmt::format( "DRC severity must be error, warning, or ignore" ) );
1022 return tl::unexpected( e );
1023 }
1024
1025 newSettings.m_DRCSeverities[ruleType] = setting;
1026 }
1027 }
1028
1029 if( rules.exclusions_size() > 0 )
1030 {
1031 newSettings.m_DrcExclusions.clear();
1032
1033 for( const kiapi::board::DrcExclusion& exclusion : rules.exclusions() )
1034 newSettings.m_DrcExclusions.insert( DRC_EXCLUSION::FromProto( exclusion ) );
1035 }
1036
1037 std::vector<BOARD_DESIGN_SETTINGS::VALIDATION_ERROR> errors = newSettings.ValidateDesignRules();
1038
1039 if( !errors.empty() )
1040 {
1041 const BOARD_DESIGN_SETTINGS::VALIDATION_ERROR& error = errors.front();
1042
1043 ApiResponseStatus e;
1044 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1045 e.set_error_message( fmt::format( "Invalid board design rules: {}: {}",
1046 error.setting_name.ToStdString(),
1047 error.error_message.ToStdString() ) );
1048 return tl::unexpected( e );
1049 }
1050
1051 board()->SetDesignSettings( newSettings );
1052
1053 if( frame() )
1054 {
1055 frame()->OnModify();
1057 }
1058
1059 HANDLER_CONTEXT<GetBoardDesignRules> getCtx = { aCtx.ClientName, GetBoardDesignRules() };
1060 *getCtx.Request.mutable_board() = aCtx.Request.board();
1061
1062 return handleGetBoardDesignRules( getCtx );
1063}
1064
1065
1068{
1069 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1070
1071 if( !documentValidation )
1072 return tl::unexpected( documentValidation.error() );
1073
1074 CustomRulesResponse response;
1075 response.set_status( CRS_NONE );
1076
1077 wxString rulesPath = board()->GetDesignRulesPath();
1078
1079 if( rulesPath.IsEmpty() || !wxFileName::IsFileReadable( rulesPath ) )
1080 return response;
1081
1082 wxFFile file( rulesPath, "r" );
1083
1084 if( !file.IsOpened() )
1085 {
1086 response.set_status( CRS_INVALID );
1087 response.set_error_text( "Failed to open custom rules file" );
1088 return response;
1089 }
1090
1091 wxString content;
1092 file.ReadAll( &content );
1093 file.Close();
1094
1095 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1096
1097 try
1098 {
1099 DRC_RULES_PARSER parser( content, "File" );
1100 parser.Parse( parsedRules, nullptr );
1101 }
1102 catch( const IO_ERROR& ioe )
1103 {
1104 response.set_status( CRS_INVALID );
1105 response.set_error_text( ioe.What().ToStdString() );
1106 return response;
1107 }
1108
1109 for( const std::shared_ptr<DRC_RULE>& rule : parsedRules )
1110 {
1111 // TODO(JE) since we now need this for both here and the rules editor, maybe it's time
1112 // to just make comment parsing part of the parser?
1113 wxString text = DRC_RULE_LOADER::ExtractRuleText( content, rule->m_Name );
1114 wxString comment = DRC_RULE_LOADER::ExtractRuleComment( text );
1115
1116 kiapi::board::CustomRule* customRule = response.add_rules();
1117
1118 if( rule->m_Condition )
1119 customRule->set_condition( rule->m_Condition->GetExpression().ToUTF8() );
1120
1121 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
1122 {
1123 board::CustomRuleConstraint* constraintProto = customRule->add_constraints();
1124 constraint.ToProto( *constraintProto );
1125 }
1126
1127 customRule->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( rule->m_Severity ) );
1128 customRule->set_name( rule->m_Name.ToUTF8() );
1129
1130 if( rule->m_LayerSource.CmpNoCase( wxS( "outer" ) ) == 0 )
1131 {
1132 customRule->set_layer_mode( kiapi::board::CRLM_OUTER );
1133 }
1134 else if( rule->m_LayerSource.CmpNoCase( wxS( "inner" ) ) == 0 )
1135 {
1136 customRule->set_layer_mode( kiapi::board::CRLM_INNER );
1137 }
1138 else if( !rule->m_LayerSource.IsEmpty() )
1139 {
1140 int layer = LSET::NameToLayer( rule->m_LayerSource );
1141
1142 if( layer != UNDEFINED_LAYER && layer != UNSELECTED_LAYER && layer < PCB_LAYER_ID_COUNT )
1143 {
1144 customRule->set_single_layer(
1146 }
1147 }
1148
1149 if( !comment.IsEmpty() )
1150 customRule->set_comments( comment );
1151 }
1152
1153 response.set_status( CRS_VALID );
1154 return response;
1155}
1156
1157
1160{
1161 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1162
1163 if( !documentValidation )
1164 return tl::unexpected( documentValidation.error() );
1165
1166 wxString rulesPath = board()->GetDesignRulesPath();
1167
1168 if( aCtx.Request.rules_size() == 0 )
1169 {
1170 if( wxFileName::FileExists( rulesPath ) )
1171 {
1172 if( !wxRemoveFile( rulesPath ) )
1173 {
1174 CustomRulesResponse response;
1175 response.set_status( CRS_INVALID );
1176 response.set_error_text( "Failed to remove custom rules file" );
1177 return response;
1178 }
1179 }
1180
1181 CustomRulesResponse response;
1182 response.set_status( CRS_NONE );
1183 return response;
1184 }
1185
1186 wxString rulesText;
1187 rulesText << "(version 2)\n";
1188
1189 for( const board::CustomRule& rule : aCtx.Request.rules() )
1190 {
1191 wxString serializationError;
1192 wxString serializedRule = DRC_RULE::FormatRuleFromProto( rule, &serializationError );
1193
1194 if( serializedRule.IsEmpty() )
1195 {
1196 CustomRulesResponse response;
1197 response.set_status( CRS_INVALID );
1198
1199 if( serializationError.IsEmpty() )
1200 response.set_error_text( "Failed to serialize custom rule" );
1201 else
1202 response.set_error_text( serializationError.ToUTF8() );
1203
1204 return response;
1205 }
1206
1207 rulesText << "\n" << serializedRule;
1208 }
1209
1210 // Validate generated file text before writing so callers get parser errors in response.
1211 try
1212 {
1213 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1214 DRC_RULES_PARSER parser( rulesText, "SetCustomDesignRules" );
1215 parser.Parse( parsedRules, nullptr );
1216 }
1217 catch( const IO_ERROR& ioe )
1218 {
1219 CustomRulesResponse response;
1220 response.set_status( CRS_INVALID );
1221 response.set_error_text( ioe.What().ToStdString() );
1222 return response;
1223 }
1224
1225 wxFFile file( rulesPath, "w" );
1226
1227 if( !file.IsOpened() )
1228 {
1229 CustomRulesResponse response;
1230 response.set_status( CRS_INVALID );
1231 response.set_error_text( "Failed to open custom rules file for writing" );
1232 return response;
1233 }
1234
1235 if( !file.Write( rulesText ) )
1236 {
1237 file.Close();
1238
1239 CustomRulesResponse response;
1240 response.set_status( CRS_INVALID );
1241 response.set_error_text( "Failed to write custom rules file" );
1242 return response;
1243 }
1244
1245 file.Close();
1246
1247 HANDLER_CONTEXT<GetCustomDesignRules> getCtx = { aCtx.ClientName, GetCustomDesignRules() };
1248 *getCtx.Request.mutable_board() = aCtx.Request.board();
1249 return handleGetCustomDesignRules( getCtx );
1250}
1251
1252
1255{
1256 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1257 !documentValidation )
1258 {
1259 return tl::unexpected( documentValidation.error() );
1260 }
1261
1262 VECTOR2I origin;
1263 const BOARD_DESIGN_SETTINGS& settings = board()->GetDesignSettings();
1264
1265 switch( aCtx.Request.type() )
1266 {
1267 case BOT_GRID:
1268 origin = settings.GetGridOrigin();
1269 break;
1270
1271 case BOT_DRILL:
1272 origin = settings.GetAuxOrigin();
1273 break;
1274
1275 default:
1276 case BOT_UNKNOWN:
1277 {
1278 ApiResponseStatus e;
1279 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1280 e.set_error_message( "Unexpected origin type" );
1281 return tl::unexpected( e );
1282 }
1283 }
1284
1285 types::Vector2 reply;
1286 PackVector2( reply, origin );
1287 return reply;
1288}
1289
1292{
1293 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1294 return tl::unexpected( *busy );
1295
1296 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1297 !documentValidation )
1298 {
1299 return tl::unexpected( documentValidation.error() );
1300 }
1301
1302 VECTOR2I origin = UnpackVector2( aCtx.Request.origin() );
1303
1304 switch( aCtx.Request.type() )
1305 {
1306 case BOT_GRID:
1307 {
1308 PCB_EDIT_FRAME* f = frame();
1309
1310 if( f )
1311 {
1312 frame()->CallAfter(
1313 [f, origin]()
1314 {
1315 // gridSetOrigin takes ownership and frees this
1316 VECTOR2D* dorigin = new VECTOR2D( origin );
1317 TOOL_MANAGER* mgr = f->GetToolManager();
1318 mgr->RunAction( PCB_ACTIONS::gridSetOrigin, dorigin );
1319 f->Refresh();
1320 } );
1321 }
1322 else
1323 {
1324 board()->GetDesignSettings().SetGridOrigin( origin );
1325 }
1326
1327 break;
1328 }
1329
1330 case BOT_DRILL:
1331 {
1332 PCB_EDIT_FRAME* f = frame();
1333
1334 if( f )
1335 {
1336 frame()->CallAfter(
1337 [f, origin]()
1338 {
1339 TOOL_MANAGER* mgr = f->GetToolManager();
1340 mgr->RunAction( PCB_ACTIONS::drillSetOrigin, origin );
1341 f->Refresh();
1342 } );
1343 }
1344 else
1345 {
1346 board()->GetDesignSettings().SetAuxOrigin( origin );
1347 }
1348
1349 break;
1350 }
1351
1352 default:
1353 case BOT_UNKNOWN:
1354 {
1355 ApiResponseStatus e;
1356 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1357 e.set_error_message( "Unexpected origin type" );
1358 return tl::unexpected( e );
1359 }
1360 }
1361
1362 return Empty();
1363}
1364
1365
1368{
1369 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1370 !documentValidation )
1371 {
1372 return tl::unexpected( documentValidation.error() );
1373 }
1374
1375 BoardLayerNameResponse response;
1376
1378
1379 response.set_name( board()->GetLayerName( id ) );
1380
1381 return response;
1382}
1383
1384
1387{
1388 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1389 !documentValidation )
1390 {
1391 return tl::unexpected( documentValidation.error() );
1392 }
1393
1394 BoardLayerResponse response;
1395
1396 PCB_LAYER_ID id = board()->GetLayerID( wxString::FromUTF8( aCtx.Request.name() ) );
1397 response.set_layer( ToProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( id ) );
1398
1399 return response;
1400}
1401
1402
1403std::optional<TITLE_BLOCK*> API_HANDLER_PCB::getTitleBlock( const DocumentSpecifier& aDocument )
1404{
1405 return &context()->GetBoard()->GetTitleBlock();
1406}
1407
1408
1409std::optional<PAGE_INFO> API_HANDLER_PCB::getPageSettings( const DocumentSpecifier& aDocument )
1410{
1411 return context()->GetBoard()->GetPageSettings();
1412}
1413
1414
1415bool API_HANDLER_PCB::setPageSettings( const DocumentSpecifier& aDocument, const PAGE_INFO& aPageInfo )
1416{
1417 context()->GetBoard()->SetPageSettings( aPageInfo );
1418 return true;
1419}
1420
1421
1426
1427
1428void API_HANDLER_PCB::setDrawingSheetFileName( const wxString& aFileName )
1429{
1431
1432 if( frame() )
1434}
1435
1436
1438{
1440
1441 if( frame() )
1442 {
1443 frame()->Refresh();
1444 frame()->OnModify();
1446 }
1447}
1448
1449
1452{
1453 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
1454 return tl::unexpected( documentValidation.error() );
1455
1456 GetDocumentModifiedStateResponse response;
1457 response.set_state( pcbContext()->IsContentModified() ? DocumentModifiedState::DMS_MODIFIED
1458 : DocumentModifiedState::DMS_UNMODIFIED );
1459 return response;
1460}
1461
1462
1464{
1465 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1466
1467 if( !documentValidation )
1468 return tl::unexpected( documentValidation.error() );
1469
1470 NetsResponse response;
1471 BOARD* board = this->board();
1472
1473 std::set<wxString> netclassFilter;
1474
1475 for( const std::string& nc : aCtx.Request.netclass_filter() )
1476 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
1477
1478 for( NETINFO_ITEM* net : board->GetNetInfo() )
1479 {
1480 NETCLASS* nc = net->GetNetClass();
1481
1482 if( !netclassFilter.empty() && nc )
1483 {
1484 bool inClass = false;
1485
1486 for( const wxString& filter : netclassFilter )
1487 {
1488 if( nc->ContainsNetclassWithName( filter ) )
1489 {
1490 inClass = true;
1491 break;
1492 }
1493 }
1494
1495 if( !inClass )
1496 continue;
1497 }
1498
1499 board::types::Net* netProto = response.add_nets();
1500 netProto->set_name( net->GetNetname() );
1501 netProto->mutable_code()->set_value( net->GetNetCode() );
1502 }
1503
1504 return response;
1505}
1506
1507
1510{
1511 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1512 return tl::unexpected( *busy );
1513
1514 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1515 {
1516 ApiResponseStatus e;
1517 e.set_status( ApiStatusCode::AS_UNHANDLED );
1518 return tl::unexpected( e );
1519 }
1520
1521 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1522 const bool filterByType = aCtx.Request.types_size() > 0;
1523
1524 if( filterByType && types.empty() )
1525 {
1526 ApiResponseStatus e;
1527 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1528 e.set_error_message( "none of the requested types are valid for a Board object" );
1529 return tl::unexpected( e );
1530 }
1531
1532 std::set<KICAD_T> typeFilter( types.begin(), types.end() );
1533 std::vector<BOARD_CONNECTED_ITEM*> sourceItems;
1534
1535 for( const types::KIID& id : aCtx.Request.items() )
1536 {
1537 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
1538 {
1539 if( BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( *item ) )
1540 sourceItems.emplace_back( connected );
1541 }
1542 }
1543
1544 if( sourceItems.empty() )
1545 {
1546 ApiResponseStatus e;
1547 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1548 e.set_error_message( "none of the requested IDs were found or valid connected items" );
1549 return tl::unexpected( e );
1550 }
1551
1552 GetItemsResponse response;
1553 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
1554 std::set<KIID> insertedItems;
1555
1556 for( BOARD_CONNECTED_ITEM* source : sourceItems )
1557 {
1558 for( BOARD_CONNECTED_ITEM* connected : conn->GetConnectedItems( source ) )
1559 {
1560 if( filterByType && !typeFilter.contains( connected->Type() ) )
1561 continue;
1562
1563 if( !insertedItems.insert( connected->m_Uuid ).second )
1564 continue;
1565
1566 connected->Serialize( *response.add_items() );
1567 }
1568 }
1569
1570 response.set_status( ItemRequestStatus::IRS_OK );
1571 return response;
1572}
1573
1574
1576 const HANDLER_CONTEXT<GetItemsByNet>& aCtx )
1577{
1578 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1579 return tl::unexpected( *busy );
1580
1581 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1582 {
1583 ApiResponseStatus e;
1584 e.set_status( ApiStatusCode::AS_UNHANDLED );
1585 return tl::unexpected( e );
1586 }
1587
1588 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1589 const bool filterByType = aCtx.Request.types_size() > 0;
1590
1591 if( filterByType && types.empty() )
1592 {
1593 ApiResponseStatus e;
1594 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1595 e.set_error_message( "none of the requested types are valid for a Board object" );
1596 return tl::unexpected( e );
1597 }
1598
1599 if( !filterByType )
1601
1602 GetItemsResponse response;
1603 BOARD* board = this->board();
1604 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
1605 std::set<KIID> insertedItems;
1606
1607 const NETINFO_LIST& nets = board->GetNetInfo();
1608
1609 for( const board::types::Net& net : aCtx.Request.nets() )
1610 {
1611 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
1612
1613 if( !netInfo )
1614 continue;
1615
1616 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( netInfo->GetNetCode(), types ) )
1617 {
1618 if( !insertedItems.insert( item->m_Uuid ).second )
1619 continue;
1620
1621 item->Serialize( *response.add_items() );
1622 }
1623 }
1624
1625 response.set_status( ItemRequestStatus::IRS_OK );
1626 return response;
1627}
1628
1629
1632{
1633 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1634 return tl::unexpected( *busy );
1635
1636 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1637 {
1638 ApiResponseStatus e;
1639 e.set_status( ApiStatusCode::AS_UNHANDLED );
1640 return tl::unexpected( e );
1641 }
1642
1643 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1644 const bool filterByType = aCtx.Request.types_size() > 0;
1645
1646 if( filterByType && types.empty() )
1647 {
1648 ApiResponseStatus e;
1649 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1650 e.set_error_message( "none of the requested types are valid for a Board object" );
1651 return tl::unexpected( e );
1652 }
1653
1654 if( !filterByType )
1656
1657 std::set<wxString> requestedClasses;
1658
1659 for( const std::string& netClass : aCtx.Request.net_classes() )
1660 requestedClasses.insert( wxString( netClass.c_str(), wxConvUTF8 ) );
1661
1662 GetItemsResponse response;
1663 BOARD* board = this->board();
1664 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
1665 std::set<KIID> insertedItems;
1666
1667 for( NETINFO_ITEM* net : board->GetNetInfo() )
1668 {
1669 if( !net )
1670 continue;
1671
1672 NETCLASS* nc = net->GetNetClass();
1673
1674 if( !requestedClasses.empty() )
1675 {
1676 if( !nc )
1677 continue;
1678
1679 bool inClass = false;
1680
1681 for( const wxString& filter : requestedClasses )
1682 {
1683 if( nc->ContainsNetclassWithName( filter ) )
1684 {
1685 inClass = true;
1686 break;
1687 }
1688 }
1689
1690 if( !inClass )
1691 continue;
1692 }
1693
1694 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( net->GetNetCode(), types ) )
1695 {
1696 if( !insertedItems.insert( item->m_Uuid ).second )
1697 continue;
1698
1699 item->Serialize( *response.add_items() );
1700 }
1701 }
1702
1703 response.set_status( ItemRequestStatus::IRS_OK );
1704 return response;
1705}
1706
1707
1710{
1711 NetClassForNetsResponse response;
1712
1713 BOARD* board = this->board();
1714 const NETINFO_LIST& nets = board->GetNetInfo();
1715 for( const board::types::Net& net : aCtx.Request.net() )
1716 {
1717 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
1718
1719 if( !netInfo )
1720 continue;
1721
1722 auto [pair, rc] = response.mutable_classes()->insert( { net.name(), {} } );
1723 netInfo->GetNetClass()->Serialize( pair->second );
1724 }
1725
1726 return response;
1727}
1728
1729
1731{
1732 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1733 return tl::unexpected( *busy );
1734
1735 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1736
1737 if( !documentValidation )
1738 return tl::unexpected( documentValidation.error() );
1739
1740 TOOL_MANAGER* mgr = toolManager();
1741
1742 // A frame's tool manager always carries the zone filler tool; headless sessions start with a
1743 // bare tool manager and register it on first use, like the CLI jobs do.
1744 if( !mgr->FindTool( ZONE_FILLER_TOOL_NAME ) )
1745 mgr->RegisterTool( new ZONE_FILLER_TOOL );
1746
1747 if( aCtx.Request.zones().empty() )
1748 {
1749 if( frame() )
1750 {
1751 frame()->CallAfter( [mgr]()
1752 {
1754 } );
1755 }
1756 else
1757 {
1758 // Headless sessions have no event loop to defer to; fill synchronously through the
1759 // same tool the CLI jobs use.
1760 mgr->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, nullptr, true );
1761 }
1762 }
1763 else
1764 {
1765 std::vector<ZONE*> toFill;
1766
1767 for( const types::KIID& id : aCtx.Request.zones() )
1768 {
1769 std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) );
1770
1771 if( !item || ( *item )->Type() != PCB_ZONE_T )
1772 {
1773 ApiResponseStatus e;
1774 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1775 e.set_error_message( fmt::format( "zone with ID {} not found on the board", id.value() ) );
1776 return tl::unexpected( e );
1777 }
1778
1779 ZONE* zone = static_cast<ZONE*>( *item );
1780
1781 // The filler silently skips rule areas, which would turn this into a false success
1782 if( zone->GetIsRuleArea() )
1783 {
1784 ApiResponseStatus e;
1785 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1786 e.set_error_message( fmt::format( "zone with ID {} is a rule area and cannot be filled",
1787 id.value() ) );
1788 return tl::unexpected( e );
1789 }
1790
1791 // A repeated id would enqueue concurrent fill tasks for the same zone
1792 if( !alg::contains( toFill, zone ) )
1793 toFill.push_back( zone );
1794 }
1795
1796 std::unique_ptr<COMMIT> commit = createCommit();
1797 ZONE_FILLER filler( board(), commit.get() );
1798
1799 if( !filler.Fill( toFill ) )
1800 {
1801 commit->Revert();
1802
1803 ApiResponseStatus e;
1804 e.set_status( ApiStatusCode::AS_UNKNOWN );
1805 e.set_error_message( "zone fill failed" );
1806 return tl::unexpected( e );
1807 }
1808
1809 commit->Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
1810
1811 // Push skipped connectivity, so run the same post-fill refresh as the interactive fill
1812 mgr->GetTool<ZONE_FILLER_TOOL>()->PostFillRefresh( frame() == nullptr );
1813 }
1814
1815 return Empty();
1816}
1817
1818
1820{
1821 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1822 return tl::unexpected( *busy );
1823
1824 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1825
1826 if( !documentValidation )
1827 return tl::unexpected( documentValidation.error() );
1828
1829 wxFileName netlistPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.netlist_path() ) ) );
1830
1831 if( !netlistPath.IsOk() || !netlistPath.FileExists() )
1832 {
1833 ApiResponseStatus e;
1834 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1835 e.set_error_message(
1836 fmt::format( "netlist file '{}' could not be opened", netlistPath.GetFullPath().ToStdString() ) );
1837 return tl::unexpected( e );
1838 }
1839
1840 PCB_CONTEXT* ctx = pcbContext();
1842
1843 const bool lookupByTimestamp = aCtx.Request.match_mode() != NetlistMatchMode::NMM_REFERENCE;
1844
1846 netlist.SetFindByTimeStamp( lookupByTimestamp );
1847 netlist.SetReplaceFootprints( aCtx.Request.update_footprints() );
1848
1849 if( !ctx->ReadNetlistFromFile( netlistPath.GetFullPath(), netlist, reporter ) )
1850 {
1851 ApiResponseStatus e;
1852 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1853 e.set_error_message( fmt::format( "unable to handle netlist file '{}': {}",
1854 netlistPath.GetFullPath().ToStdString(),
1855 reporter.GetMessages().ToStdString() ) );
1856 return tl::unexpected( e );
1857 }
1858
1859 std::unique_ptr<BOARD_NETLIST_UPDATER> updater = ctx->MakeNetlistUpdater();
1860
1861 updater->SetReporter( &reporter );
1862 updater->SetIsDryRun( aCtx.Request.dry_run() );
1863 updater->SetLookupByTimestamp( lookupByTimestamp );
1864 updater->SetDeleteUnusedFootprints( aCtx.Request.delete_extra_footprints() );
1865 updater->SetReplaceFootprints( aCtx.Request.update_footprints() );
1866 updater->SetTransferGroups( aCtx.Request.transfer_groups() );
1867 updater->SetOverrideLocks( aCtx.Request.override_locks() );
1868 updater->SetUpdateFields( true );
1869
1870 const bool success = updater->UpdateNetlist( netlist );
1871
1872 if( !aCtx.Request.dry_run() && success )
1873 ctx->OnNetlistChanged( *updater );
1874
1875 ImportNetlistResponse response;
1876 response.set_report( reporter.GetMessages().ToUTF8() );
1877 response.set_error_count( updater->GetErrorCount() );
1878 response.set_warning_count( updater->GetWarningCount() );
1879 response.set_new_footprint_count( updater->GetNewFootprintCount() );
1880 return response;
1881}
1882
1883
1886{
1887 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetBoardEditorAppearanceSettings" ) )
1888 return tl::unexpected( *headless );
1889
1890 BoardEditorAppearanceSettings reply;
1891
1892 // TODO: might be nice to put all these things in one place and have it derive SERIALIZABLE
1893
1894 const PCB_DISPLAY_OPTIONS& displayOptions = frame()->GetDisplayOptions();
1895
1896 reply.set_inactive_layer_display( ToProtoEnum<HIGH_CONTRAST_MODE, InactiveLayerDisplayMode>(
1897 displayOptions.m_ContrastModeDisplay ) );
1898 reply.set_net_color_display(
1900
1901 reply.set_board_flip( frame()->GetCanvas()->GetView()->IsMirroredX()
1902 ? BoardFlipMode::BFM_FLIPPED_X
1903 : BoardFlipMode::BFM_NORMAL );
1904
1905 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
1906
1907 reply.set_ratsnest_display( ToProtoEnum<RATSNEST_MODE, RatsnestDisplayMode>(
1908 editorSettings->m_Display.m_RatsnestMode ) );
1909
1910 return reply;
1911}
1912
1913
1916{
1917 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetBoardEditorAppearanceSettings" ) )
1918 return tl::unexpected( *headless );
1919
1920 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1921 return tl::unexpected( *busy );
1922
1924 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
1925 const BoardEditorAppearanceSettings& newSettings = aCtx.Request.settings();
1926
1927 options.m_ContrastModeDisplay =
1928 FromProtoEnum<HIGH_CONTRAST_MODE>( newSettings.inactive_layer_display() );
1929 options.m_NetColorMode =
1930 FromProtoEnum<NET_COLOR_MODE>( newSettings.net_color_display() );
1931 options.m_FlipBoardView = newSettings.board_flip() == BoardFlipMode::BFM_FLIPPED_X;
1932
1933 editorSettings->m_Display.m_RatsnestMode =
1934 FromProtoEnum<RATSNEST_MODE>( newSettings.ratsnest_display() );
1935
1936 frame()->SetDisplayOptions( options );
1938 frame()->GetCanvas()->Refresh();
1939
1940 return Empty();
1941}
1942
1943
1946{
1947 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1948
1949 if( !documentValidation )
1950 return tl::unexpected( documentValidation.error() );
1951
1952 const PCB_PLOT_PARAMS& plotOpts = board()->GetPlotOptions();
1953
1954 BoardPlotSettingsResponse response;
1955 BoardPlotSettings* settings = response.mutable_plot_settings();
1956
1957 board::PackLayerSet( *settings->mutable_layers(), plotOpts.GetLayerSelection() );
1958
1959 for( PCB_LAYER_ID layer : plotOpts.GetPlotOnAllLayersSequence() )
1960 settings->add_common_layers( ToProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( layer ) );
1961
1962 settings->set_mirror( plotOpts.GetMirror() );
1963 settings->set_black_and_white( plotOpts.GetBlackAndWhite() );
1964 settings->set_negative( plotOpts.GetNegative() );
1965 settings->set_scale( plotOpts.GetScale() );
1966
1967 settings->set_sketch_pads_on_fab_layers( plotOpts.GetSketchPadsOnFabLayers() );
1968 settings->set_hide_dnp_footprints_on_fab_layers( plotOpts.GetHideDNPFPsOnFabLayers() );
1969 settings->set_sketch_dnp_footprints_on_fab_layers( plotOpts.GetSketchDNPFPsOnFabLayers() );
1970 settings->set_crossout_dnp_footprints_on_fab_layers( plotOpts.GetCrossoutDNPFPsOnFabLayers() );
1971
1972 settings->set_plot_footprint_values( plotOpts.GetPlotValue() );
1973 settings->set_plot_reference_designators( plotOpts.GetPlotReference() );
1974 settings->set_plot_drawing_sheet( plotOpts.GetPlotFrameRef() );
1975 settings->set_subtract_solder_mask_from_silk( plotOpts.GetSubtractMaskFromSilk() );
1976 settings->set_plot_pad_numbers( plotOpts.GetPlotPadNumbers() );
1977
1978 settings->set_drill_marks( ToProtoEnum<DRILL_MARKS, PlotDrillMarks>( plotOpts.GetDrillMarksType() ) );
1979 settings->set_use_drill_origin( plotOpts.GetUseAuxOrigin() );
1980
1981 return response;
1982}
1983
1984
1986{
1987 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1988 return tl::unexpected( *busy );
1989
1990 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1991
1992 if( !documentValidation )
1993 return tl::unexpected( documentValidation.error() );
1994
1995 const BoardPlotSettings& settings = aCtx.Request.plot_settings();
1996 PCB_PLOT_PARAMS plotOpts = board()->GetPlotOptions();
1997
1998 plotOpts.SetLayerSelection( board::UnpackLayerSet( settings.layers() ) );
1999
2000 LSEQ commonLayers;
2001
2002 for( int layer : settings.common_layers() )
2003 {
2004 PCB_LAYER_ID layerId =
2005 FromProtoEnum<PCB_LAYER_ID, board::types::BoardLayer>( static_cast<board::types::BoardLayer>( layer ) );
2006
2007 if( !IsPcbLayer( layerId ) )
2008 {
2009 ApiResponseStatus e;
2010 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2011 e.set_error_message( fmt::format( "SetBoardPlotSettings contains an invalid layer {}",
2012 magic_enum::enum_name( layerId ) ) );
2013 return tl::unexpected( e );
2014 }
2015
2016 commonLayers.push_back( layerId );
2017 }
2018
2019 plotOpts.SetPlotOnAllLayersSequence( commonLayers );
2020
2021 plotOpts.SetMirror( settings.mirror() );
2022 plotOpts.SetBlackAndWhite( settings.black_and_white() );
2023 plotOpts.SetNegative( settings.negative() );
2024 plotOpts.SetScale( settings.scale() );
2025
2026 plotOpts.SetSketchPadsOnFabLayers( settings.sketch_pads_on_fab_layers() );
2027 plotOpts.SetHideDNPFPsOnFabLayers( settings.hide_dnp_footprints_on_fab_layers() );
2028 plotOpts.SetSketchDNPFPsOnFabLayers( settings.sketch_dnp_footprints_on_fab_layers() );
2029 plotOpts.SetCrossoutDNPFPsOnFabLayers( settings.crossout_dnp_footprints_on_fab_layers() );
2030
2031 plotOpts.SetPlotValue( settings.plot_footprint_values() );
2032 plotOpts.SetPlotReference( settings.plot_reference_designators() );
2033 plotOpts.SetPlotFrameRef( settings.plot_drawing_sheet() );
2034 plotOpts.SetSubtractMaskFromSilk( settings.subtract_solder_mask_from_silk() );
2035 plotOpts.SetPlotPadNumbers( settings.plot_pad_numbers() );
2036
2037 plotOpts.SetDrillMarksType( FromProtoEnum<DRILL_MARKS>( settings.drill_marks() ) );
2038 plotOpts.SetUseAuxOrigin( settings.use_drill_origin() );
2039
2040 board()->SetPlotOptions( plotOpts );
2041
2042 if( frame() )
2043 frame()->OnModify();
2044
2045 return Empty();
2046}
2047
2048
2051{
2052 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2053 return tl::unexpected( *busy );
2054
2055 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
2056
2057 if( !documentValidation )
2058 return tl::unexpected( documentValidation.error() );
2059
2060 SEVERITY severity = FromProtoEnum<SEVERITY>( aCtx.Request.severity() );
2061 int layer = severity == RPT_SEVERITY_WARNING ? LAYER_DRC_WARNING : LAYER_DRC_ERROR;
2063
2064 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( code );
2065
2066 drcItem->SetErrorMessage( wxString::FromUTF8( aCtx.Request.message() ) );
2067
2068 RC_ITEM::KIIDS ids;
2069
2070 for( const auto& id : aCtx.Request.items() )
2071 ids.emplace_back( KIID( id.value() ) );
2072
2073 if( !ids.empty() )
2074 drcItem->SetItems( ids );
2075
2076 const auto& pos = aCtx.Request.position();
2077 VECTOR2I position( static_cast<int>( pos.x_nm() ), static_cast<int>( pos.y_nm() ) );
2078
2079 PCB_MARKER* marker = new PCB_MARKER( drcItem, position, layer );
2080
2081 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
2082 commit->Add( marker );
2083 commit->Push( wxS( "API injected DRC marker" ) );
2084
2085 InjectDrcErrorResponse response;
2086 response.mutable_marker()->set_value( marker->GetUUID().AsStdString() );
2087
2088 return response;
2089}
2090
2091
2092std::optional<ApiResponseStatus> ValidateUnitsInchMm( types::Units aUnits,
2093 const std::string& aCommandName )
2094{
2095 if( aUnits == types::Units::U_INCH || aUnits == types::Units::U_MM
2096 || aUnits == types::Units::U_UNKNOWN )
2097 {
2098 return std::nullopt;
2099 }
2100
2101 ApiResponseStatus e;
2102 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2103 e.set_error_message( fmt::format( "{} supports only inch and mm units", aCommandName ) );
2104 return e;
2105}
2106
2107
2108std::optional<ApiResponseStatus>
2109ValidatePaginationModeForSingleOrPerFile( kiapi::board::jobs::BoardJobPaginationMode aMode,
2110 const std::string& aCommandName )
2111{
2112 if( aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_UNKNOWN
2113 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_ALL_LAYERS_ONE_PAGE
2114 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_EACH_LAYER_OWN_FILE )
2115 {
2116 return std::nullopt;
2117 }
2118
2119 ApiResponseStatus e;
2120 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2121 e.set_error_message( fmt::format( "{} does not support EACH_LAYER_OWN_PAGE pagination mode",
2122 aCommandName ) );
2123 return e;
2124}
2125
2126
2127std::optional<ApiResponseStatus> ApplyBoardPlotSettings( const BoardPlotSettings& aSettings,
2128 JOB_EXPORT_PCB_PLOT& aJob )
2129{
2130 for( int layer : aSettings.layers() )
2131 {
2133 static_cast<board::types::BoardLayer>( layer ) );
2134
2135 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2136 {
2137 ApiResponseStatus e;
2138 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2139 e.set_error_message( "Board plot settings contain an invalid layer" );
2140 return e;
2141 }
2142
2143 aJob.m_plotLayerSequence.push_back( layerId );
2144 }
2145
2146 for( int layer : aSettings.common_layers() )
2147 {
2149 static_cast<board::types::BoardLayer>( layer ) );
2150
2151 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2152 {
2153 ApiResponseStatus e;
2154 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2155 e.set_error_message( "Board plot settings contain an invalid common layer" );
2156 return e;
2157 }
2158
2159 aJob.m_plotOnAllLayersSequence.push_back( layerId );
2160 }
2161
2162 aJob.m_colorTheme = wxString::FromUTF8( aSettings.color_theme() );
2163 aJob.m_drawingSheet = wxString::FromUTF8( aSettings.drawing_sheet() );
2164 aJob.m_variant = wxString::FromUTF8( aSettings.variant() );
2165
2166 aJob.m_mirror = aSettings.mirror();
2167 aJob.m_blackAndWhite = aSettings.black_and_white();
2168 aJob.m_negative = aSettings.negative();
2169 aJob.m_scale = aSettings.scale();
2170
2171 aJob.m_sketchPadsOnFabLayers = aSettings.sketch_pads_on_fab_layers();
2172 aJob.m_hideDNPFPsOnFabLayers = aSettings.hide_dnp_footprints_on_fab_layers();
2173 aJob.m_sketchDNPFPsOnFabLayers = aSettings.sketch_dnp_footprints_on_fab_layers();
2174 aJob.m_crossoutDNPFPsOnFabLayers = aSettings.crossout_dnp_footprints_on_fab_layers();
2175
2176 aJob.m_plotFootprintValues = aSettings.plot_footprint_values();
2177 aJob.m_plotRefDes = aSettings.plot_reference_designators();
2178 aJob.m_plotDrawingSheet = aSettings.plot_drawing_sheet();
2179 aJob.m_subtractSolderMaskFromSilk = aSettings.subtract_solder_mask_from_silk();
2180 aJob.m_plotPadNumbers = aSettings.plot_pad_numbers();
2181
2182 aJob.m_drillShapeOption = FromProtoEnum<DRILL_MARKS>( aSettings.drill_marks() );
2183
2184 aJob.m_useDrillOrigin = aSettings.use_drill_origin();
2185 aJob.m_checkZonesBeforePlot = aSettings.check_zones_before_plot();
2186
2187 return std::nullopt;
2188}
2189
2190
2192{
2193 types::RunJobResponse response;
2195
2196 if( !aContext || !aContext->GetKiway() )
2197 {
2198 response.set_status( types::JobStatus::JS_ERROR );
2199 response.set_message( "Internal error" );
2200 wxCHECK_MSG( false, response, "context missing valid kiway in ExecuteBoardJob?" );
2201 return response;
2202 }
2203
2204 int exitCode = aContext->GetKiway()->ProcessJob( KIWAY::FACE_PCB, &aJob, &reporter );
2205
2206 for( const JOB_OUTPUT& output : aJob.GetOutputs() )
2207 response.add_output_path( output.m_outputPath.ToUTF8() );
2208
2209 if( exitCode == 0 )
2210 {
2211 response.set_status( types::JobStatus::JS_SUCCESS );
2212 return response;
2213 }
2214
2215 response.set_status( types::JobStatus::JS_ERROR );
2216 response.set_message( fmt::format( "Board export job '{}' failed with exit code {}: {}",
2217 aJob.GetType(), exitCode,
2218 reporter.GetMessages().ToStdString() ) );
2219 return response;
2220}
2221
2222
2225{
2226 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2227 return tl::unexpected( *busy );
2228
2229 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2230
2231 if( !documentValidation )
2232 return tl::unexpected( documentValidation.error() );
2233
2236 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2237
2239
2240 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2241 job.m_3dparams.m_NetFilter = wxString::FromUTF8( aCtx.Request.net_filter() );
2242 job.m_3dparams.m_ComponentFilter = wxString::FromUTF8( aCtx.Request.component_filter() );
2243
2244 job.m_hasUserOrigin = aCtx.Request.has_user_origin();
2245 job.m_3dparams.m_Origin = VECTOR2D( aCtx.Request.origin().x_nm(), aCtx.Request.origin().y_nm() );
2246
2247 job.m_3dparams.m_Overwrite = aCtx.Request.overwrite();
2248 job.m_3dparams.m_UseGridOrigin = aCtx.Request.use_grid_origin();
2249 job.m_3dparams.m_UseDrillOrigin = aCtx.Request.use_drill_origin();
2250 job.m_3dparams.m_UseDefinedOrigin = aCtx.Request.use_defined_origin() || aCtx.Request.has_user_origin();
2251 job.m_3dparams.m_UsePcbCenterOrigin = aCtx.Request.use_pcb_center_origin();
2252
2253 job.m_3dparams.m_IncludeUnspecified = aCtx.Request.include_unspecified();
2254 job.m_3dparams.m_IncludeDNP = aCtx.Request.include_dnp();
2255 job.m_3dparams.m_SubstModels = aCtx.Request.substitute_models();
2256
2257 job.m_3dparams.m_BoardOutlinesChainingEpsilon = aCtx.Request.board_outlines_chaining_epsilon();
2258 job.m_3dparams.m_BoardOnly = aCtx.Request.board_only();
2259 job.m_3dparams.m_CutViasInBody = aCtx.Request.cut_vias_in_body();
2260 job.m_3dparams.m_ExportBoardBody = aCtx.Request.export_board_body();
2261 job.m_3dparams.m_ExportComponents = aCtx.Request.export_components();
2262 job.m_3dparams.m_ExportTracksVias = aCtx.Request.export_tracks_and_vias();
2263 job.m_3dparams.m_ExportPads = aCtx.Request.export_pads();
2264 job.m_3dparams.m_ExportZones = aCtx.Request.export_zones();
2265 job.m_3dparams.m_ExportInnerCopper = aCtx.Request.export_inner_copper();
2266 job.m_3dparams.m_ExportSilkscreen = aCtx.Request.export_silkscreen();
2267 job.m_3dparams.m_ExportSoldermask = aCtx.Request.export_soldermask();
2268 job.m_3dparams.m_FuseShapes = aCtx.Request.fuse_shapes();
2269 job.m_3dparams.m_FillAllVias = aCtx.Request.fill_all_vias();
2270 job.m_3dparams.m_OptimizeStep = aCtx.Request.optimize_step();
2271 job.m_3dparams.m_ExtraPadThickness = aCtx.Request.extra_pad_thickness();
2272
2274
2275 job.m_vrmlModelDir = wxString::FromUTF8( aCtx.Request.vrml_model_dir() );
2276 job.m_vrmlRelativePaths = aCtx.Request.vrml_relative_paths();
2277
2278 return ExecuteBoardJob( pcbContext(), job );
2279}
2280
2281
2284{
2285 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2286 return tl::unexpected( *busy );
2287
2288 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2289
2290 if( !documentValidation )
2291 return tl::unexpected( documentValidation.error() );
2292
2293 JOB_PCB_RENDER job;
2295 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2296
2299 job.m_bgStyle = FromProtoEnum<JOB_PCB_RENDER::BG_STYLE>( aCtx.Request.background_style() );
2300
2301 job.m_width = aCtx.Request.width();
2302 job.m_height = aCtx.Request.height();
2303 job.m_appearancePreset = aCtx.Request.appearance_preset();
2304 job.m_useBoardStackupColors = aCtx.Request.use_board_stackup_colors();
2305
2307
2308 job.m_zoom = aCtx.Request.zoom();
2309 job.m_perspective = aCtx.Request.perspective();
2310
2311 job.m_rotation = UnpackVector3D( aCtx.Request.rotation() );
2312 job.m_pan = UnpackVector3D( aCtx.Request.pan() );
2313 job.m_pivot = UnpackVector3D( aCtx.Request.pivot() );
2314
2315 job.m_proceduralTextures = aCtx.Request.procedural_textures();
2316 job.m_floor = aCtx.Request.floor();
2317 job.m_antiAlias = aCtx.Request.anti_alias();
2318 job.m_postProcess = aCtx.Request.post_process();
2319
2320 job.m_lightTopIntensity = UnpackVector3D( aCtx.Request.light_top_intensity() );
2321 job.m_lightBottomIntensity = UnpackVector3D( aCtx.Request.light_bottom_intensity() );
2322 job.m_lightCameraIntensity = UnpackVector3D( aCtx.Request.light_camera_intensity() );
2323 job.m_lightSideIntensity = UnpackVector3D( aCtx.Request.light_side_intensity() );
2324 job.m_lightSideElevation = aCtx.Request.light_side_elevation();
2325
2326 return ExecuteBoardJob( pcbContext(), job );
2327}
2328
2329
2332{
2333 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2334 return tl::unexpected( *busy );
2335
2336 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2337
2338 if( !documentValidation )
2339 return tl::unexpected( documentValidation.error() );
2340
2343 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2344
2345 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2346 return tl::unexpected( *err );
2347
2348 job.m_fitPageToBoard = aCtx.Request.fit_page_to_board();
2349 job.m_precision = aCtx.Request.precision();
2350
2351 if( std::optional<ApiResponseStatus> paginationError =
2353 "RunBoardJobExportSvg" ) )
2354 {
2355 return tl::unexpected( *paginationError );
2356 }
2357
2359
2360 return ExecuteBoardJob( pcbContext(), job );
2361}
2362
2363
2366{
2367 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2368 return tl::unexpected( *busy );
2369
2370 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2371
2372 if( !documentValidation )
2373 return tl::unexpected( documentValidation.error() );
2374
2377 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2378
2379 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2380 return tl::unexpected( *err );
2381
2382 job.m_plotGraphicItemsUsingContours = aCtx.Request.plot_graphic_items_using_contours();
2383 job.m_polygonMode = aCtx.Request.polygon_mode();
2384
2385 if( std::optional<ApiResponseStatus> unitError =
2386 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDxf" ) )
2387 {
2388 return tl::unexpected( *unitError );
2389 }
2390
2392
2393 if( std::optional<ApiResponseStatus> paginationError =
2395 "RunBoardJobExportDxf" ) )
2396 {
2397 return tl::unexpected( *paginationError );
2398 }
2399
2401
2402 return ExecuteBoardJob( pcbContext(), job );
2403}
2404
2405
2408{
2409 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2410 return tl::unexpected( *busy );
2411
2412 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2413
2414 if( !documentValidation )
2415 return tl::unexpected( documentValidation.error() );
2416
2419 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2420
2421 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2422 return tl::unexpected( *err );
2423
2424 job.m_pdfFrontFPPropertyPopups = aCtx.Request.front_footprint_property_popups();
2425 job.m_pdfBackFPPropertyPopups = aCtx.Request.back_footprint_property_popups();
2426 job.m_pdfMetadata = aCtx.Request.include_metadata();
2427 job.m_pdfSingle = aCtx.Request.single_document();
2428 job.m_pdfBackgroundColor = wxString::FromUTF8( aCtx.Request.background_color() );
2429
2431
2432 return ExecuteBoardJob( pcbContext(), job );
2433}
2434
2435
2438{
2439 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2440 return tl::unexpected( *busy );
2441
2442 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2443
2444 if( !documentValidation )
2445 return tl::unexpected( documentValidation.error() );
2446
2449 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2450
2451 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2452 return tl::unexpected( *err );
2453
2454 if( std::optional<ApiResponseStatus> paginationError =
2456 "RunBoardJobExportPs" ) )
2457 {
2458 return tl::unexpected( *paginationError );
2459 }
2460
2462
2463 job.m_trackWidthCorrection = aCtx.Request.track_width_correction();
2464 job.m_XScaleAdjust = aCtx.Request.x_scale_adjust();
2465 job.m_YScaleAdjust = aCtx.Request.y_scale_adjust();
2466 job.m_forceA4 = aCtx.Request.force_a4();
2467 job.m_useGlobalSettings = aCtx.Request.use_global_settings();
2468
2469 return ExecuteBoardJob( pcbContext(), job );
2470}
2471
2472
2475{
2476 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2477 return tl::unexpected( *busy );
2478
2479 if( HANDLER_RESULT<bool> validation = validateDocument( aCtx.Request.job_settings().document() ); !validation )
2480 return tl::unexpected( validation.error() );
2481
2484 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2485
2486 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2487 return tl::unexpected( *err );
2488
2489 if( std::optional<ApiResponseStatus> paginationError =
2490 ValidatePaginationModeForSingleOrPerFile( aCtx.Request.page_mode(), "RunBoardJobExportPng" ) )
2491 {
2492 return tl::unexpected( *paginationError );
2493 }
2494
2496
2497 if( aCtx.Request.has_dpi() )
2498 {
2499 int dpi = aCtx.Request.dpi();
2500
2501 if( dpi < MIN_PNG_DPI || dpi > MAX_PNG_DPI )
2502 {
2503 ApiResponseStatus status;
2504 status.set_status( ApiStatusCode::AS_BAD_REQUEST );
2505 status.set_error_message( fmt::format( "dpi must be between {} and {}", MIN_PNG_DPI, MAX_PNG_DPI ) );
2506 return tl::unexpected( status );
2507 }
2508
2509 job.m_dpi = dpi;
2510 }
2511
2512 // Unknown -> default AA on
2513 job.m_antialias = aCtx.Request.antialiasing() != types::AntialiasingMode::AAM_NONE;
2514
2515 return ExecuteBoardJob( pcbContext(), job );
2516}
2517
2518
2521{
2522 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2523 return tl::unexpected( *busy );
2524
2525 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2526
2527 if( !documentValidation )
2528 return tl::unexpected( documentValidation.error() );
2529
2532 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2533
2534 job.m_useBoardPlotParams = aCtx.Request.use_board_plot_params();
2535
2536 if( !job.m_useBoardPlotParams )
2537 {
2538 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2539 return tl::unexpected( *err );
2540 }
2541
2542 job.m_createJobsFile = aCtx.Request.create_gerber_job_file();
2543 job.m_includeNetlistAttributes = aCtx.Request.include_netlist_attributes();
2544 job.m_useX2Format = aCtx.Request.use_x2_format();
2545 job.m_disableApertureMacros = aCtx.Request.disable_aperture_macros();
2546 job.m_useProtelFileExtension = aCtx.Request.use_protel_file_extensions();
2547
2548 switch( aCtx.Request.precision() )
2549 {
2550 default:
2551 case GerberPrecision::GP_5: job.m_precision = 5; break;
2552 case GerberPrecision::GP_6: job.m_precision = 6; break;
2553 }
2554
2555 return ExecuteBoardJob( pcbContext(), job );
2556}
2557
2558
2561{
2562 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2563 return tl::unexpected( *busy );
2564
2565 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2566
2567 if( !documentValidation )
2568 return tl::unexpected( documentValidation.error() );
2569
2572 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2573
2575
2576 if( std::optional<ApiResponseStatus> unitError =
2577 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDrill" ) )
2578 {
2579 return tl::unexpected( *unitError );
2580 }
2581
2585
2586 if( aCtx.Request.has_excellon() )
2587 {
2588 const ExcellonFormatOptions& excellonOptions = aCtx.Request.excellon();
2589
2590 if( excellonOptions.has_mirror_y() )
2591 job.m_excellonMirrorY = excellonOptions.mirror_y();
2592
2593 if( excellonOptions.has_minimal_header() )
2594 job.m_excellonMinimalHeader = excellonOptions.minimal_header();
2595
2596 if( excellonOptions.has_combine_pth_npth() )
2597 job.m_excellonCombinePTHNPTH = excellonOptions.combine_pth_npth();
2598
2599 if( excellonOptions.has_route_oval_holes() )
2600 job.m_excellonOvalDrillRoute = excellonOptions.route_oval_holes();
2601 }
2602
2603 if( aCtx.Request.map_format() != DrillMapFormat::DMF_UNKNOWN )
2604 {
2605 job.m_generateMap = true;
2607 }
2608
2609 job.m_gerberPrecision = aCtx.Request.gerber_precision() == DrillGerberPrecision::DGP_4_5 ? 5 : 6;
2610
2611 if( aCtx.Request.has_gerber_generate_tenting() )
2612 job.m_generateTenting = aCtx.Request.gerber_generate_tenting();
2613
2614 if( aCtx.Request.report_format() != DrillReportFormat::DRF_UNKNOWN )
2615 {
2616 job.m_generateReport = true;
2617
2618 if( aCtx.Request.has_report_filename() )
2619 job.m_reportPath = wxString::FromUTF8( aCtx.Request.report_filename() );
2620 }
2621
2622 return ExecuteBoardJob( pcbContext(), job );
2623}
2624
2625
2628{
2629 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2630 return tl::unexpected( *busy );
2631
2632 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2633
2634 if( !documentValidation )
2635 return tl::unexpected( documentValidation.error() );
2636
2639 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2640
2641 if( aCtx.Request.has_use_drill_place_file_origin() )
2642 job.m_useDrillPlaceFileOrigin = aCtx.Request.use_drill_place_file_origin();
2643
2644 job.m_smdOnly = aCtx.Request.smd_only();
2645 job.m_excludeFootprintsWithTh = aCtx.Request.exclude_footprints_with_th();
2646 job.m_excludeDNP = aCtx.Request.exclude_dnp();
2647 job.m_excludeBOM = aCtx.Request.exclude_from_bom();
2648 job.m_negateBottomX = aCtx.Request.negate_bottom_x();
2649 job.m_singleFile = aCtx.Request.single_file();
2650 job.m_nakedFilename = aCtx.Request.naked_filename();
2651 if( aCtx.Request.has_include_board_edge_for_gerber() )
2652 job.m_gerberBoardEdge = aCtx.Request.include_board_edge_for_gerber();
2653
2654 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2655
2657
2658 if( std::optional<ApiResponseStatus> unitError =
2659 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportPosition" ) )
2660 {
2661 return tl::unexpected( *unitError );
2662 }
2663
2666
2667 return ExecuteBoardJob( pcbContext(), job );
2668}
2669
2670
2673{
2674 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2675 return tl::unexpected( *busy );
2676
2677 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2678
2679 if( !documentValidation )
2680 return tl::unexpected( documentValidation.error() );
2681
2684 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2685
2686 job.m_flipBottomPads = aCtx.Request.flip_bottom_pads();
2687 job.m_useIndividualShapes = aCtx.Request.use_individual_shapes();
2688 job.m_storeOriginCoords = aCtx.Request.store_origin_coords();
2689 job.m_useDrillOrigin = aCtx.Request.use_drill_origin();
2690 job.m_useUniquePins = aCtx.Request.use_unique_pins();
2691
2692 return ExecuteBoardJob( pcbContext(), job );
2693}
2694
2695
2698{
2699 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2700 return tl::unexpected( *busy );
2701
2702 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2703
2704 if( !documentValidation )
2705 return tl::unexpected( documentValidation.error() );
2706
2709 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2710
2711 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
2712 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2713 if( aCtx.Request.has_precision() )
2714 job.m_precision = aCtx.Request.precision();
2715
2716 job.m_compress = aCtx.Request.compress();
2717 job.m_colInternalId = wxString::FromUTF8( aCtx.Request.internal_id_column() );
2718 job.m_colMfgPn = wxString::FromUTF8( aCtx.Request.manufacturer_part_number_column() );
2719 job.m_colMfg = wxString::FromUTF8( aCtx.Request.manufacturer_column() );
2720 job.m_colDistPn = wxString::FromUTF8( aCtx.Request.distributor_part_number_column() );
2721 job.m_colDist = wxString::FromUTF8( aCtx.Request.distributor_column() );
2722 job.m_bomRev = wxString::FromUTF8( aCtx.Request.bom_revision() );
2723
2724 if( std::optional<ApiResponseStatus> unitError =
2725 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportIpc2581" ) )
2726 {
2727 return tl::unexpected( *unitError );
2728 }
2729
2732
2733 return ExecuteBoardJob( pcbContext(), job );
2734}
2735
2736
2739{
2740 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2741 return tl::unexpected( *busy );
2742
2743 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2744
2745 if( !documentValidation )
2746 return tl::unexpected( documentValidation.error() );
2747
2750 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2751
2752 return ExecuteBoardJob( pcbContext(), job );
2753}
2754
2755
2758{
2759 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2760 return tl::unexpected( *busy );
2761
2762 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2763
2764 if( !documentValidation )
2765 return tl::unexpected( documentValidation.error() );
2766
2769 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2770
2771 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
2772 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2773 if( aCtx.Request.has_precision() )
2774 job.m_precision = aCtx.Request.precision();
2775
2776 if( std::optional<ApiResponseStatus> unitError =
2777 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportODB" ) )
2778 {
2779 return tl::unexpected( *unitError );
2780 }
2781
2784
2785 return ExecuteBoardJob( pcbContext(), job );
2786}
2787
2788
2791{
2792 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2793 return tl::unexpected( *busy );
2794
2795 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2796
2797 if( !documentValidation )
2798 return tl::unexpected( documentValidation.error() );
2799
2802 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2803
2805
2806 if( std::optional<ApiResponseStatus> unitError =
2807 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportStats" ) )
2808 {
2809 return tl::unexpected( *unitError );
2810 }
2811
2813
2814 job.m_excludeFootprintsWithoutPads = aCtx.Request.exclude_footprints_without_pads();
2815 job.m_subtractHolesFromBoardArea = aCtx.Request.subtract_holes_from_board_area();
2816 job.m_subtractHolesFromCopperAreas = aCtx.Request.subtract_holes_from_copper_areas();
2817
2818 return ExecuteBoardJob( pcbContext(), job );
2819}
2820
2821
2824{
2825 wxLogTrace( traceApi, "Received announce from frame %d at %s",
2826 aCtx.Request.frame_type(), aCtx.Request.socket_path() );
2827
2828 CROSS_PROBE_CLIENT::RegisterPeer( static_cast<FRAME_T>( aCtx.Request.frame_type() ),
2829 aCtx.Request.socket_path() );
2830
2831 CrossProbeAnnounceResponse response;
2832 response.set_status( CPS_OK );
2833 return response;
2834}
2835
2836
2838{
2839 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SyncSelection" ) )
2840 return tl::unexpected( *headless );
2841
2842 SyncSelectionResponse response;
2843
2845
2846 if( !settings.on_selection && aCtx.Request.context() != SyncSelectionContext::SSC_EXPLICIT )
2847 {
2848 response.set_status( CPS_DISABLED );
2849 response.set_message( "implicit selection sync disabled by user" );
2850 return response;
2851 }
2852
2853 std::vector<BOARD_ITEM*> items =
2855
2856 frame()->m_ProbingSchToPcb = true; // recursion guard
2857
2858 if( aCtx.Request.mode() == SyncSelectionMode::SSM_ITEMS_AND_NETS )
2860 else
2862
2863 // Update 3D viewer highlighting
2864 frame()->Update3DView( false, frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
2865
2866 frame()->m_ProbingSchToPcb = false;
2867
2868 if( settings.flash_selection )
2869 {
2870 wxLogTrace( traceCrossProbeFlash, "MAIL_SELECTION(_FORCE) PCB: flash enabled, items=%zu", items.size() );
2871 if( items.empty() )
2872 {
2873 wxLogTrace( traceCrossProbeFlash, "MAIL_SELECTION(_FORCE) PCB: nothing to flash" );
2874 }
2875 else
2876 {
2877 std::vector<BOARD_ITEM*> boardItems;
2878 std::copy( items.begin(), items.end(), std::back_inserter( boardItems ) );
2879 frame()->StartCrossProbeFlash( boardItems );
2880 }
2881 }
2882 else
2883 {
2884 wxLogTrace( traceCrossProbeFlash, "MAIL_SELECTION(_FORCE) PCB: flash disabled" );
2885 }
2886
2887 response.set_status( CPS_OK );
2888 return response;
2889}
2890
2891
2893 const HANDLER_CONTEXT<HighlightNets>& aCtx )
2894{
2895 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "HighlightNets" ) )
2896 return tl::unexpected( *headless );
2897
2898 HighlightNetsResponse response;
2899 CROSS_PROBING_SETTINGS& crossProbingSettings = frame()->GetPcbNewSettings()->m_CrossProbing;
2900
2902 || aCtx.ClientName == KiwayClientName )
2903 {
2904 if( !crossProbingSettings.auto_highlight )
2905 {
2906 response.set_status( CPS_DISABLED );
2907 response.set_message( "net highlight cross-probing disabled by user" );
2908 return response;
2909 }
2910 }
2911
2912 std::vector<wxString> nets;
2913
2914 for( const std::string& name : aCtx.Request.net_name() )
2915 nets.emplace_back( wxString::FromUTF8( name ) );
2916
2918
2919 response.set_status( CPS_OK );
2920 return response;
2921}
2922
2923
2925{
2926 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
2927 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
2928
2929 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2930 return tl::unexpected( *busy );
2931
2932 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
2933 return tl::unexpected( documentValidation.error() );
2934
2936 VariantsResponse response;
2937
2938 response.mutable_document()->CopyFrom( aCtx.Request.document() );
2939
2940 for( const wxString& name : board->GetVariantNames() )
2941 {
2942 types::DesignVariant* var = response.add_variants();
2943 var->set_name( name.ToUTF8() );
2944 var->set_description( board->GetVariantDescription( name ).ToUTF8() );
2945 }
2946
2947 return response;
2948}
2949
2950
2952{
2953 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
2954 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
2955
2956 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2957 return tl::unexpected( *busy );
2958
2959 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
2960 return tl::unexpected( documentValidation.error() );
2961
2963
2964 wxString name = wxString::FromUTF8( aCtx.Request.name() );
2965
2966 if( name.IsEmpty() || name.CmpNoCase( GetDefaultVariantName() ) == 0 )
2967 {
2968 ApiResponseStatus e;
2969 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2970 e.set_error_message( fmt::format( "'{}' is not a valid variant name", aCtx.Request.name() ) );
2971 return tl::unexpected( e );
2972 }
2973
2974 if( board->HasVariant( name ) )
2975 {
2976 ApiResponseStatus e;
2977 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2978 e.set_error_message( fmt::format( "a variant named '{}' already exists", aCtx.Request.name() ) );
2979 return tl::unexpected( e );
2980 }
2981
2982 board->AddVariant( name );
2983
2984 if( aCtx.Request.has_description() )
2985 board->SetVariantDescription( name, wxString::FromUTF8( aCtx.Request.description() ) );
2986
2987 if( frame() )
2989
2990 return Empty();
2991}
2992
2993
2995{
2996 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
2997 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
2998
2999 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3000 return tl::unexpected( *busy );
3001
3002 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
3003 return tl::unexpected( documentValidation.error() );
3004
3006
3007 wxString name = wxString::FromUTF8( aCtx.Request.name() );
3008
3009 if( name.IsEmpty() || name.CmpNoCase( GetDefaultVariantName() ) == 0 )
3010 {
3011 ApiResponseStatus e;
3012 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3013 e.set_error_message( fmt::format( "'{}' is not a valid variant name", aCtx.Request.name() ) );
3014 return tl::unexpected( e );
3015 }
3016
3017 if( !board->HasVariant( name ) )
3018 {
3019 ApiResponseStatus e;
3020 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3021 e.set_error_message( fmt::format( "no variant named '{}' exists", aCtx.Request.name() ) );
3022 return tl::unexpected( e );
3023 }
3024
3025 board->DeleteVariant( name );
3026
3027 if( frame() )
3029
3030 return Empty();
3031}
3032
3033
3035{
3036 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
3037 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
3038
3039 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3040 return tl::unexpected( *busy );
3041
3042 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
3043 return tl::unexpected( documentValidation.error() );
3044
3046
3047 wxString oldName = wxString::FromUTF8( aCtx.Request.old_name() );
3048 wxString newName = wxString::FromUTF8( aCtx.Request.new_name() );
3049
3050 if( oldName.IsEmpty() || oldName.CmpNoCase( GetDefaultVariantName() ) == 0 )
3051 {
3052 ApiResponseStatus e;
3053 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3054 e.set_error_message( fmt::format( "'{}' is not a valid variant name", aCtx.Request.old_name() ) );
3055 return tl::unexpected( e );
3056 }
3057
3058 if( newName.IsEmpty() || newName.CmpNoCase( GetDefaultVariantName() ) == 0 )
3059 {
3060 ApiResponseStatus e;
3061 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3062 e.set_error_message( fmt::format( "'{}' is not a valid variant name", aCtx.Request.new_name() ) );
3063 return tl::unexpected( e );
3064 }
3065
3066 if( !board->HasVariant( oldName ) )
3067 {
3068 ApiResponseStatus e;
3069 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3070 e.set_error_message( fmt::format( "no variant named '{}' exists", aCtx.Request.old_name() ) );
3071 return tl::unexpected( e );
3072 }
3073
3074 if( board->HasVariant( newName ) )
3075 {
3076 ApiResponseStatus e;
3077 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3078 e.set_error_message( fmt::format( "a variant named '{}' already exists", aCtx.Request.new_name() ) );
3079 return tl::unexpected( e );
3080 }
3081
3082 board->RenameVariant( oldName, newName );
3083
3084 if( frame() )
3086
3087 return Empty();
3088}
3089
3090
3092{
3093 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
3094 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
3095
3096 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3097 return tl::unexpected( *busy );
3098
3099 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
3100 return tl::unexpected( documentValidation.error() );
3101
3103
3104 wxString oldName = wxString::FromUTF8( aCtx.Request.old_name() );
3105 wxString newName = wxString::FromUTF8( aCtx.Request.new_name() );
3106
3107 if( oldName.IsEmpty() || oldName.CmpNoCase( GetDefaultVariantName() ) == 0 )
3108 {
3109 ApiResponseStatus e;
3110 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3111 e.set_error_message( fmt::format( "'{}' is not a valid variant name", aCtx.Request.old_name() ) );
3112 return tl::unexpected( e );
3113 }
3114
3115 if( newName.IsEmpty() || newName.CmpNoCase( GetDefaultVariantName() ) == 0 )
3116 {
3117 ApiResponseStatus e;
3118 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3119 e.set_error_message( fmt::format( "'{}' is not a valid variant name", aCtx.Request.new_name() ) );
3120 return tl::unexpected( e );
3121 }
3122
3123 if( !board->HasVariant( oldName ) )
3124 {
3125 ApiResponseStatus e;
3126 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3127 e.set_error_message( fmt::format( "no variant named '{}' exists", aCtx.Request.old_name() ) );
3128 return tl::unexpected( e );
3129 }
3130
3131 if( board->HasVariant( newName ) )
3132 {
3133 ApiResponseStatus e;
3134 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3135 e.set_error_message( fmt::format( "a variant named '{}' already exists", aCtx.Request.new_name() ) );
3136 return tl::unexpected( e );
3137 }
3138
3139 board->CopyVariant( oldName, newName,
3140 aCtx.Request.has_new_description() ? wxString::FromUTF8( aCtx.Request.new_description() )
3141 : wxString() );
3142
3143 if( frame() )
3145
3146 return Empty();
3147}
3148
3149
3151{
3152 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
3153 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
3154
3155 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3156 return tl::unexpected( *busy );
3157
3158 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
3159 return tl::unexpected( documentValidation.error() );
3160
3162
3163 wxString name = wxString::FromUTF8( aCtx.Request.name() );
3164
3165 if( name.IsEmpty() || name.CmpNoCase( GetDefaultVariantName() ) == 0 || !board->HasVariant( name ) )
3166 {
3167 ApiResponseStatus e;
3168 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3169 e.set_error_message( fmt::format( "no variant named '{}' exists", aCtx.Request.name() ) );
3170 return tl::unexpected( e );
3171 }
3172
3173 board->SetVariantDescription( name, wxString::FromUTF8( aCtx.Request.description() ) );
3174
3175 return Empty();
3176}
3177
3178
3180{
3181 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
3182 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
3183
3184 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3185 return tl::unexpected( *busy );
3186
3187 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
3188 return tl::unexpected( documentValidation.error() );
3189
3191
3192 if( aCtx.Request.has_name() && !aCtx.Request.name().empty() )
3193 {
3194 if( wxString name = wxString::FromUTF8( aCtx.Request.name() ); !board->HasVariant( name ) )
3195 {
3196 ApiResponseStatus e;
3197 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3198 e.set_error_message( fmt::format( "no variant named '{}' exists", aCtx.Request.name() ) );
3199 return tl::unexpected( e );
3200 }
3201 }
3202
3203 wxString varName = aCtx.Request.has_name() ? wxString::FromUTF8( aCtx.Request.name() ) : wxString();
3204
3205 if( frame() )
3206 frame()->SetCurrentVariant( varName );
3207 else
3208 board->SetCurrentVariant( varName );
3209
3210 return Empty();
3211}
3212
3213
3216{
3217 if( aCtx.Request.document().type() != DocumentType::DOCTYPE_PCB )
3218 return tl::unexpected( MakeResponseStatus( AS_UNHANDLED ) );
3219
3220 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() ); !documentValidation )
3221 return tl::unexpected( documentValidation.error() );
3222
3223 CurrentVariantResponse response;
3224
3225 if( wxString current = pcbContext()->GetBoard()->GetCurrentVariant(); !current.IsEmpty() )
3226 response.set_name( current.ToUTF8() );
3227
3228 return response;
3229}
3230
3231
3234{
3235 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
3236 return tl::unexpected( *busy );
3237
3238 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
3239 {
3240 ApiResponseStatus e;
3241 e.set_status( ApiStatusCode::AS_UNHANDLED );
3242 return tl::unexpected( e );
3243 }
3244
3245 LIB_ID libId = UnpackLibId( aCtx.Request.lib_id() );
3246
3247 if( !libId.IsValid() )
3248 {
3249 ApiResponseStatus e;
3250 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3251 e.set_error_message( "lib_id must specify both a library nickname and an entry name" );
3252 return tl::unexpected( e );
3253 }
3254
3256
3257 // TODO update if we support inner layer footprints in the future
3258 if( !IsExternalCopperLayer( layer ) )
3259 {
3260 ApiResponseStatus e;
3261 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3262 e.set_error_message( "layer must be F_Cu or B_Cu" );
3263 return tl::unexpected( e );
3264 }
3265
3266 std::unique_ptr<FOOTPRINT> footprint( LoadFootprintFromProject( board(), libId ) );
3267
3268 if( !footprint )
3269 {
3270 ApiResponseStatus e;
3271 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
3272 e.set_error_message( fmt::format( "footprint '{}' not found", libId.Format().wx_str() ) );
3273 return tl::unexpected( e );
3274 }
3275
3276 footprint->SetUuid( KIID() );
3277 footprint->RunOnChildren(
3278 []( BOARD_ITEM* aChild )
3279 {
3280 aChild->ResetUuid();
3281 },
3283
3284 footprint->SetParent( board() );
3285
3286 footprint->SetPosition( UnpackVector2( aCtx.Request.position() ) );
3287
3288 if( aCtx.Request.has_orientation() )
3289 footprint->SetOrientationDegrees( aCtx.Request.orientation().value_degrees() );
3290
3291 footprint->SetLayerAndFlip( layer );
3292
3293 BOARD_COMMIT* commit = static_cast<BOARD_COMMIT*>( getCurrentCommit( aCtx.ClientName ) );
3294 FOOTPRINT* placed = footprint.release();
3295 commit->Add( placed );
3296
3297 if( !m_activeClients.contains( aCtx.ClientName ) )
3298 pushCurrentCommit( aCtx.ClientName, _( "Placed footprint via API" ) );
3299
3300 PlaceFromLibraryResponse response;
3301 response.mutable_header()->CopyFrom( aCtx.Request.header() );
3302 placed->Serialize( *response.mutable_item() );
3303
3304 return response;
3305}
const char * name
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition api_handler.h:45
std::optional< ApiResponseStatus > ValidatePaginationModeForSingleOrPerFile(kiapi::board::jobs::BoardJobPaginationMode aMode, const std::string &aCommandName)
HANDLER_RESULT< Empty > unpackEmbeddedFiles(EMBEDDED_FILES &aOutput, const common::types::EmbeddedFiles &aProto)
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(PCB_CONTEXT *aContext, JOB &aJob)
static const std::vector< KICAD_T > s_allowedBoardTypes
BASE_SCREEN class implementation.
#define ZONE_FILL_OP
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION gridSetOrigin
Definition actions.h:191
API_HANDLER_BOARD(std::shared_ptr< BOARD_CONTEXT > aContext, EDA_BASE_FRAME *aFrame=nullptr)
std::unique_ptr< COMMIT > createCommit() override
Override this to create an appropriate COMMIT subclass for the frame in question.
TOOL_MANAGER * toolManager() const
std::vector< KICAD_T > parseRequestedItemTypes(const google::protobuf::RepeatedField< int > &aTypes)
PROJECT & project() const
void pushCurrentCommit(const std::string &aClientName, const wxString &aMessage) override
BOARD * board() const
BOARD_CONTEXT * context() const
std::optional< ApiResponseStatus > checkForHeadless(const std::string &aCommandName) const
std::optional< BOARD_ITEM * > getItemById(const KIID &aId) const
HANDLER_RESULT< bool > validateDocument(const DocumentSpecifier &aDocument)
HANDLER_RESULT< types::PageSettings > handleSetPageSettings(const HANDLER_CONTEXT< commands::SetPageSettings > &aCtx)
HANDLER_RESULT< std::optional< KIID > > validateItemHeaderDocument(const kiapi::common::types::ItemHeader &aHeader)
If the header is valid, returns the item container.
HANDLER_RESULT< types::PageSettings > handleGetPageSettings(const HANDLER_CONTEXT< commands::GetPageSettings > &aCtx)
COMMIT * getCurrentCommit(const std::string &aClientName)
std::set< std::string > m_activeClients
std::map< std::string, std::pair< KIID, std::unique_ptr< COMMIT > > > m_commits
virtual std::optional< ApiResponseStatus > checkForBusy()
Checks if the editor can accept commands.
EDA_BASE_FRAME * m_frame
HANDLER_RESULT< ImportNetlistResponse > handleImportNetlist(const HANDLER_CONTEXT< ImportNetlist > &aCtx)
HANDLER_RESULT< BoardPlotSettingsResponse > handleGetBoardPlotSettings(const HANDLER_CONTEXT< GetBoardPlotSettings > &aCtx)
HANDLER_RESULT< Empty > handleDeleteVariant(const HANDLER_CONTEXT< commands::DeleteVariant > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPdf(const HANDLER_CONTEXT< RunBoardJobExportPdf > &aCtx)
HANDLER_RESULT< google::protobuf::Empty > handleSetEmbeddedFiles(const HANDLER_CONTEXT< SetEmbeddedFiles > &aCtx)
HANDLER_RESULT< commands::CurrentVariantResponse > handleGetCurrentVariant(const HANDLER_CONTEXT< commands::GetCurrentVariant > &aCtx)
HANDLER_RESULT< BoardDesignRulesResponse > handleSetBoardDesignRules(const HANDLER_CONTEXT< SetBoardDesignRules > &aCtx)
API_HANDLER_PCB(PCB_EDIT_FRAME *aFrame)
HANDLER_RESULT< Empty > handleAddVariant(const HANDLER_CONTEXT< commands::AddVariant > &aCtx)
HANDLER_RESULT< commands::GetItemsResponse > handleGetConnectedItems(const HANDLER_CONTEXT< GetConnectedItems > &aCtx)
HANDLER_RESULT< commands::VariantsResponse > handleGetVariants(const HANDLER_CONTEXT< commands::GetVariants > &aCtx)
HANDLER_RESULT< types::Vector2 > handleGetBoardOrigin(const HANDLER_CONTEXT< GetBoardOrigin > &aCtx)
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsByNetClass(const HANDLER_CONTEXT< GetItemsByNetClass > &aCtx)
HANDLER_RESULT< NetClassForNetsResponse > handleGetNetClassForNets(const HANDLER_CONTEXT< GetNetClassForNets > &aCtx)
HANDLER_RESULT< commands::CrossProbeAnnounceResponse > handleCrossProbeAnnounce(const HANDLER_CONTEXT< commands::CrossProbeAnnounce > &aCtx)
HANDLER_RESULT< Empty > handleCopyVariant(const HANDLER_CONTEXT< commands::CopyVariant > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportIpcD356(const HANDLER_CONTEXT< RunBoardJobExportIpcD356 > &aCtx)
PCB_CONTEXT * pcbContext() const
HANDLER_RESULT< BoardDesignRulesResponse > handleGetBoardDesignRules(const HANDLER_CONTEXT< GetBoardDesignRules > &aCtx)
HANDLER_RESULT< commands::GetDocumentModifiedStateResponse > handleGetDocumentModifiedState(const HANDLER_CONTEXT< commands::GetDocumentModifiedState > &aCtx) override
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< types::RunJobResponse > handleRunBoardJobExport3D(const HANDLER_CONTEXT< RunBoardJobExport3D > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPng(const HANDLER_CONTEXT< RunBoardJobExportPng > &aCtx)
HANDLER_RESULT< Empty > handleSetCurrentVariant(const HANDLER_CONTEXT< commands::SetCurrentVariant > &aCtx)
void setDrawingSheetFileName(const wxString &aFileName) override
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsByNet(const HANDLER_CONTEXT< GetItemsByNet > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportIpc2581(const HANDLER_CONTEXT< RunBoardJobExportIpc2581 > &aCtx)
std::optional< TITLE_BLOCK * > getTitleBlock(const DocumentSpecifier &aDocument) override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPosition(const HANDLER_CONTEXT< RunBoardJobExportPosition > &aCtx)
HANDLER_RESULT< Empty > handleSetBoardOrigin(const HANDLER_CONTEXT< SetBoardOrigin > &aCtx)
HANDLER_RESULT< Empty > handleSetBoardPlotSettings(const HANDLER_CONTEXT< SetBoardPlotSettings > &aCtx)
HANDLER_RESULT< BoardLayerNameResponse > handleGetBoardLayerName(const HANDLER_CONTEXT< GetBoardLayerName > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportStats(const HANDLER_CONTEXT< RunBoardJobExportStats > &aCtx)
void onModified() override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPs(const HANDLER_CONTEXT< RunBoardJobExportPs > &aCtx)
HANDLER_RESULT< CustomRulesResponse > handleGetCustomDesignRules(const HANDLER_CONTEXT< GetCustomDesignRules > &aCtx)
bool setPageSettings(const DocumentSpecifier &aDocument, const PAGE_INFO &aPageInfo) override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportSvg(const HANDLER_CONTEXT< RunBoardJobExportSvg > &aCtx)
HANDLER_RESULT< commands::GetOpenDocumentsResponse > handleGetOpenDocuments(const HANDLER_CONTEXT< commands::GetOpenDocuments > &aCtx)
HANDLER_RESULT< commands::SyncSelectionResponse > handleSyncSelection(const HANDLER_CONTEXT< commands::SyncSelection > &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< Empty > handleSetVariantDescription(const HANDLER_CONTEXT< commands::SetVariantDescription > &aCtx)
HANDLER_RESULT< Empty > handleSaveDocument(const HANDLER_CONTEXT< commands::SaveDocument > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportDrill(const HANDLER_CONTEXT< RunBoardJobExportDrill > &aCtx)
HANDLER_RESULT< commands::GetItemsResponse > handleGetItems(const HANDLER_CONTEXT< commands::GetItems > &aCtx)
HANDLER_RESULT< kiapi::common::commands::PlaceFromLibraryResponse > handlePlaceFootprintFromLibrary(const HANDLER_CONTEXT< kiapi::board::commands::PlaceFootprintFromLibrary > &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 > handleRenameVariant(const HANDLER_CONTEXT< commands::RenameVariant > &aCtx)
std::optional< PAGE_INFO > getPageSettings(const DocumentSpecifier &aDocument) override
HANDLER_RESULT< Empty > handleSetBoardEditorAppearanceSettings(const HANDLER_CONTEXT< SetBoardEditorAppearanceSettings > &aCtx)
tl::expected< bool, ApiResponseStatus > validateDocumentInternal(const DocumentSpecifier &aDocument) const override
HANDLER_RESULT< Empty > handleRefillZones(const HANDLER_CONTEXT< RefillZones > &aCtx)
HANDLER_RESULT< commands::HighlightNetsResponse > handleHighlightNets(const HANDLER_CONTEXT< commands::HighlightNets > &aCtx)
HANDLER_RESULT< google::protobuf::Empty > handleAddEmbeddedFiles(const HANDLER_CONTEXT< AddEmbeddedFiles > &aCtx)
HANDLER_RESULT< common::types::EmbeddedFiles > handleGetEmbeddedFiles(const HANDLER_CONTEXT< GetEmbeddedFiles > &aCtx)
HANDLER_RESULT< BoardLayerResponse > handleGetBoardLayerByName(const HANDLER_CONTEXT< GetBoardLayerByName > &aCtx)
HANDLER_RESULT< Empty > handleRevertDocument(const HANDLER_CONTEXT< commands::RevertDocument > &aCtx)
HANDLER_RESULT< CustomRulesResponse > handleSetCustomDesignRules(const HANDLER_CONTEXT< SetCustomDesignRules > &aCtx)
wxString getDrawingSheetFileName() override
void registerHandler(HANDLER_RESULT< ResponseType >(HandlerType::*aHandler)(const HANDLER_CONTEXT< RequestType > &))
Registers an API command handler for the given message types.
Definition api_handler.h:93
CROSS_PROBING_SETTINGS m_CrossProbing
static wxString m_DrawingSheetFileName
the name of the drawing sheet file, or empty to use the default drawing sheet
Definition base_screen.h:81
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual KIWAY * GetKiway() const =0
virtual BOARD * GetBoard() const =0
Container for design settings for a BOARD object.
std::map< int, SEVERITY > m_DRCSeverities
void SetGridOrigin(const VECTOR2I &aOrigin)
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
const VECTOR2I & GetGridOrigin() const
TEARDROP_PARAMETERS_LIST m_TeardropParamsList
The parameters of teardrops for the different teardrop targets (via/pad, track end).
void SetAuxOrigin(const VECTOR2I &aOrigin)
const VECTOR2I & GetAuxOrigin() const
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.
std::vector< VIA_DIMENSION > m_ViasDimensionsList
std::set< DRC_EXCLUSION, DRC_EXCLUSION_COMPARE > m_DrcExclusions
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:280
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition board.h:1014
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3825
const PAGE_INFO & GetPageSettings() const
Definition board.h:1010
void SetDesignSettings(const BOARD_DESIGN_SETTINGS &aSettings)
Definition board.cpp:1305
TITLE_BLOCK & GetTitleBlock()
Definition board.h:1016
PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition board.cpp:916
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition board.h:1011
const wxString & GetFileName() const
Definition board.h:452
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:1013
wxString GetDesignRulesPath() const
Return the absolute path to the design rules file for this board.
Definition board.cpp:435
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:751
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:68
virtual void Push(const wxString &aMessage=wxT("A commit"), int aFlags=0)=0
Execute the changes.
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
static void RegisterPeer(FRAME_T aFrameType, const std::string &aSocketPath)
void ToProto(kiapi::board::CustomRuleConstraint &aProto) const
Definition drc_rule.cpp:415
Container for an DRC exclusion, which is a PCB_MARKER plus an optional comment.
static DRC_EXCLUSION FromProto(const kiapi::board::DrcExclusion &aMessage)
const kiapi::board::DrcExclusion & ToProto() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:444
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:121
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
const std::map< wxString, std::shared_ptr< EMBEDDED_FILE > > & EmbeddedFileMap() const
Provide an iterable view of the file collection.
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
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 UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:862
Definition kiid.h:46
std::string AsStdString() const
Definition kiid.cpp:270
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:740
@ FACE_PCB
pcbnew DSO
Definition kiway.h:348
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
UTF8 Format() const
Definition lib_id.cpp:132
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:604
static int NameToLayer(wxString &aName)
Return the layer number from a layer name.
Definition lset.cpp:113
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:324
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition netclass.cpp:232
Handle the data for a net.
Definition netinfo.h:50
NETCLASS * GetNetClass()
Definition netinfo.h:101
int GetNetCode() const
Definition netinfo.h:104
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:231
NETINFO_ITEM * GetNetItem(int aNetCode) const
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
DISPLAY_OPTIONS m_Display
static TOOL_ACTION zoneFillAll
static TOOL_ACTION syncSelection
Sets selection to specified items, zooms to fit, if enabled.
Definition pcb_actions.h:62
static TOOL_ACTION drillSetOrigin
static TOOL_ACTION syncSelectionWithNets
Sets selection to specified items with connected nets, zooms to fit, if enabled.
Definition pcb_actions.h:65
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.
void SetDisplayOptions(const PCB_DISPLAY_OPTIONS &aOptions, bool aRefresh=true)
Update the current display options.
virtual void Update3DView(bool aMarkDirty, bool aRefresh, const wxString *aTitle=nullptr)
Update the 3D view, if the viewer is opened by this frame.
PCB-editor-specific context; extends BOARD_CONTEXT with save/filename operations.
Definition pcb_context.h:38
virtual bool SaveBoard()=0
virtual wxString GetCurrentFileName() const =0
virtual void OnNetlistChanged(BOARD_NETLIST_UPDATER &aUpdater)=0
Post-import board sync (nets, classes, DRC, ratsnest, new footprint placement).
virtual bool ReadNetlistFromFile(const wxString &aFilename, NETLIST &aNetlist, REPORTER &aReporter)=0
Read a netlist file and preload component footprints.
virtual void SetContentModified(bool aModified=true)=0
virtual bool SavePcbCopy(const wxString &aFileName, bool aCreateProject, bool aHeadless)=0
virtual std::unique_ptr< BOARD_NETLIST_UPDATER > MakeNetlistUpdater()=0
Create a netlist updater bound to this context's board.
bool m_FlipBoardView
true if the board is flipped to show the mirrored view
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.
The main frame for Pcbnew.
void LoadDrawingSheet()
Load the drawing sheet file.
void OnModify() override
Must be called after a board change to set the modified flag.
void StartCrossProbeFlash(const std::vector< BOARD_ITEM * > &aItems)
void UpdateVariantSelectionCtrl()
Update the variant selection dropdown with the current board's variant names.
void SetCurrentVariant(const wxString &aVariantName)
Set the current variant on the board and update the drawing sheet's cached variant name and descripti...
void UpdateUserInterface()
Update the layer manager and other widgets from the board setup (layer and items visibility,...
void HandleRemoteNetHighlight(const std::vector< wxString > &aNetNames)
const KIID GetUUID() const override
Definition pcb_marker.h:50
Parameters and options when plotting/printing a board.
bool GetNegative() const
void SetDrillMarksType(DRILL_MARKS aVal)
bool GetUseAuxOrigin() const
LSEQ GetPlotOnAllLayersSequence() const
bool GetHideDNPFPsOnFabLayers() const
void SetLayerSelection(const LSET &aSelection)
void SetPlotReference(bool aFlag)
void SetSketchPadsOnFabLayers(bool aFlag)
bool GetMirror() const
bool GetCrossoutDNPFPsOnFabLayers() const
bool GetSketchDNPFPsOnFabLayers() const
void SetPlotOnAllLayersSequence(LSEQ aSeq)
void SetPlotFrameRef(bool aFlag)
void SetSketchDNPFPsOnFabLayers(bool aFlag)
double GetScale() const
void SetPlotPadNumbers(bool aFlag)
LSET GetLayerSelection() const
bool GetPlotReference() const
void SetScale(double aVal)
void SetMirror(bool aFlag)
void SetBlackAndWhite(bool blackAndWhite)
void SetSubtractMaskFromSilk(bool aSubtract)
bool GetSketchPadsOnFabLayers() const
void SetHideDNPFPsOnFabLayers(bool aFlag)
bool GetSubtractMaskFromSilk() const
void SetPlotValue(bool aFlag)
bool GetPlotPadNumbers() const
DRILL_MARKS GetDrillMarksType() const
bool GetPlotValue() const
void SetNegative(bool aFlag)
bool GetBlackAndWhite() const
void SetUseAuxOrigin(bool aAux)
bool GetPlotFrameRef() const
void SetCrossoutDNPFPsOnFabLayers(bool aFlag)
std::vector< KIID > KIIDS
Definition rc_item.h:81
TEARDROP_PARAMETERS_LIST is a helper class to handle the list of TEARDROP_PARAMETERS needed to build ...
bool m_UseRoundShapesOnly
True to create teardrops for round shapes only.
bool m_TargetVias
True to create teardrops for vias.
bool m_TargetPTHPads
True to create teardrops for pads with holes.
bool m_TargetTrack2Track
True to create teardrops at the end of a track connected to the end of another track having a differe...
TEARDROP_PARAMETERS * GetParameters(TARGET_TD aTdType)
bool m_TargetSMDPads
True to create teardrops for pads SMD, edge connectors,.
TEARDROP_PARAMETARS is a helper class to handle parameters needed to build teardrops for a board thes...
double m_BestWidthRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxLen
max allowed length for teardrops in IU. <= 0 to disable
bool m_AllowUseTwoTracks
True to create teardrops using 2 track segments if the first in too small.
int m_TdMaxWidth
max allowed height for teardrops in IU. <= 0 to disable
double m_BestLengthRatio
The length of a teardrop as ratio between length and size of pad/via.
double m_WidthtoSizeFilterRatio
The ratio (H/D) between the via/pad size and the track width max value to create a teardrop 1....
bool m_TdOnPadsInZones
A filter to exclude pads inside zone fills.
bool m_Enabled
Flag to enable teardrops.
bool m_CurvedEdges
True if the teardrop should be curved.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Master controller class:
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
TOOL_BASE * FindTool(int aId) const
Search for a tool with given ID.
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
wxString wx_str() const
Definition utf8.cpp:41
A wrapper for reporting to a wxString object.
Definition reporter.h:242
Handle actions specific to filling copper zones.
bool Fill(const std::vector< ZONE * > &aZones, bool aCheck=false, wxWindow *aParent=nullptr)
Fills the given list of zones.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:807
PCB_DRC_CODE
Definition drc_item.h:35
@ DRCE_GENERIC_ERROR
Definition drc_item.h:94
@ DRCE_GENERIC_WARNING
Definition drc_item.h:93
static std::string ToStdString(const wxString &aStr)
#define _(s)
@ RECURSE
Definition eda_item.h:51
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition frame_type.h:29
static const std::string KiCadPcbFileExtension
const wxChar *const traceCrossProbeFlash
Flag to enable debug output for cross-probe flash operations.
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition api_utils.cpp:33
bool IsPcbLayer(int aLayer)
Test whether a layer is a valid layer for Pcbnew.
Definition layer_ids.h:692
#define MAX_CU_LAYERS
Definition layer_ids.h:172
@ LAYER_DRC_WARNING
Layer for DRC markers with #SEVERITY_WARNING.
Definition layer_ids.h:297
@ LAYER_DRC_ERROR
Layer for DRC markers with #SEVERITY_ERROR.
Definition layer_ids.h:273
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:714
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ UNSELECTED_LAYER
Definition layer_ids.h:58
@ Margin
Definition layer_ids.h:109
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
void PackEmbeddedFiles(common::types::EmbeddedFiles &aOutput, const EMBEDDED_FILES &aFiles)
bool UnpackEmbeddedFiles(EMBEDDED_FILES &aOutput, const common::types::EmbeddedFiles &aProto)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
std::vector< BOARD_ITEM * > FindItemsFromSyncSelection(const BOARD *aBoard, const google::protobuf::RepeatedPtrField< kiapi::common::commands::SelectionSpec > &aItems)
Resolve a cross-probe selection request against a board.
KICOMMON_API VECTOR3D UnpackVector3D(const types::Vector3D &aInput)
KICOMMON_API ApiResponseStatus MakeResponseStatus(ApiStatusCode aCode, const std::string &aMessage)
Definition api_utils.cpp:39
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API LIB_ID UnpackLibId(const types::LibraryIdentifier &aId)
const KICOMMON_API std::string KiwayClientName
const KICOMMON_API std::string StandaloneCrossProbeClientName
STL namespace.
std::shared_ptr< PCB_CONTEXT > CreatePcbFrameContext(PCB_EDIT_FRAME *aFrame)
Class to handle a set of BOARD_ITEMs.
FOOTPRINT * LoadFootprintFromProject(BOARD *aBoard, const LIB_ID &aFootprintId, bool aKeepUuid)
Load a footprint from the project library table and apply board default settings.
constexpr int MIN_PNG_DPI
Definition plotter_png.h:29
constexpr int MAX_PNG_DPI
Definition plotter_png.h:30
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
wxString GetDefaultVariantName()
Cross-probing behavior.
bool flash_selection
Flash newly cross-probed selection (visual attention aid).
bool on_selection
Synchronize the selection for multiple items too.
bool auto_highlight
Automatically turn on highlight mode in the target frame.
std::string ClientName
Definition api_handler.h:51
RequestMessageType Request
Definition api_handler.h:52
@ TARGET_ROUND
@ TARGET_TRACK
std::string netlist
IbisParser parser & reporter
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_CONSTRAINT_T
a geometric constraint between board items
Definition typeinfo.h:237
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_GRID_ITEM_T
a subgrid placed on a board
Definition typeinfo.h:238
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:92
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:105
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
#define ZONE_FILLER_TOOL_NAME