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 <api/api_handler_pcb.h>
27#include <api/api_pcb_utils.h>
28#include <api/api_enums.h>
29#include <api/api_utils.h>
30#include <base_screen.h>
31#include <board_commit.h>
34#include <core/kicad_algo.h>
35#include <footprint.h>
36#include <kicad_clipboard.h>
37#include <netinfo.h>
38#include <pad.h>
39#include <pcb_draw_panel_gal.h>
40#include <pcb_edit_frame.h>
41#include <pcb_group.h>
42#include <pcb_reference_image.h>
43#include <pcb_shape.h>
44#include <pcb_text.h>
45#include <pcb_textbox.h>
46#include <pcb_track.h>
47#include <pcbnew_id.h>
48#include <pcb_marker.h>
49#include <kiway.h>
50#include <drc/drc_item.h>
65#include <jobs/job_pcb_render.h>
66#include <layer_ids.h>
69#include <project.h>
70#include <tool/actions.h>
71#include <tool/tool_manager.h>
72#include <tools/pcb_actions.h>
75#include <zone.h>
76#include <zone_filler.h>
77
78#include <api/common/types/base_types.pb.h>
81#include <drc/drc_rule_parser.h>
85#include <wx/ffile.h>
86
87using namespace kiapi::common::commands;
88using types::CommandStatus;
89using types::DocumentType;
90using types::ItemRequestStatus;
91
92
97
98
99API_HANDLER_PCB::API_HANDLER_PCB( std::shared_ptr<PCB_CONTEXT> aContext, PCB_EDIT_FRAME* aFrame ) :
100 API_HANDLER_BOARD( std::move( aContext ), aFrame )
101{
107
109
118
127
134
163
166}
167
168
170{
171 return static_cast<PCB_EDIT_FRAME*>( m_frame );
172}
173
174
177{
178 if( aCtx.Request.type() != DocumentType::DOCTYPE_PCB )
179 {
180 ApiResponseStatus e;
181 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
182 e.set_status( ApiStatusCode::AS_UNHANDLED );
183 return tl::unexpected( e );
184 }
185
186 GetOpenDocumentsResponse response;
187 common::types::DocumentSpecifier doc;
188
189 wxFileName fn( pcbContext()->GetCurrentFileName() );
190
191 doc.set_type( DocumentType::DOCTYPE_PCB );
192 doc.set_board_filename( fn.GetFullName() );
193
194 doc.mutable_project()->set_name( project().GetProjectName().ToStdString() );
195 doc.mutable_project()->set_path( project().GetProjectDirectory().ToStdString() );
196
197 response.mutable_documents()->Add( std::move( doc ) );
198 return response;
199}
200
201
204{
205 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
206 return tl::unexpected( *busy );
207
208 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
209
210 if( !documentValidation )
211 return tl::unexpected( documentValidation.error() );
212
214 return Empty();
215}
216
217
220{
221 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
222 return tl::unexpected( *busy );
223
224 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
225
226 if( !documentValidation )
227 return tl::unexpected( documentValidation.error() );
228
229 wxFileName boardPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.path() ) ) );
230
231 if( !boardPath.IsOk() || !boardPath.IsDirWritable() )
232 {
233 ApiResponseStatus e;
234 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
235 e.set_error_message( fmt::format( "save path '{}' could not be opened",
236 boardPath.GetFullPath().ToStdString() ) );
237 return tl::unexpected( e );
238 }
239
240 if( boardPath.FileExists()
241 && ( !boardPath.IsFileWritable() || !aCtx.Request.options().overwrite() ) )
242 {
243 ApiResponseStatus e;
244 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
245 e.set_error_message( fmt::format( "save path '{}' exists and cannot be overwritten",
246 boardPath.GetFullPath().ToStdString() ) );
247 return tl::unexpected( e );
248 }
249
250 if( boardPath.GetExt() != FILEEXT::KiCadPcbFileExtension )
251 {
252 ApiResponseStatus e;
253 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
254 e.set_error_message( fmt::format( "save path '{}' must have a kicad_pcb extension",
255 boardPath.GetFullPath().ToStdString() ) );
256 return tl::unexpected( e );
257 }
258
259 BOARD* board = this->board();
260
261 if( board->GetFileName().Matches( boardPath.GetFullPath() ) )
262 {
264 return Empty();
265 }
266
267 bool includeProject = true;
268
269 if( aCtx.Request.has_options() )
270 includeProject = aCtx.Request.options().include_project();
271
272 pcbContext()->SavePcbCopy( boardPath.GetFullPath(), includeProject, /* aHeadless = */ true );
273
274 return Empty();
275}
276
277
280{
281 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RevertDocument" ) )
282 return tl::unexpected( *headless );
283
284 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
285 return tl::unexpected( *busy );
286
287 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
288
289 if( !documentValidation )
290 return tl::unexpected( documentValidation.error() );
291
292 wxFileName fn = project().AbsolutePath( board()->GetFileName() );
293
294 frame()->GetScreen()->SetContentModified( false );
295 frame()->ReleaseFile();
296 frame()->OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ), KICTL_REVERT );
297
298 return Empty();
299}
300
301
302tl::expected<bool, ApiResponseStatus> API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
303{
304 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
305 {
306 ApiResponseStatus e;
307 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
308 e.set_error_message( "the requested document is not a board" );
309 return tl::unexpected( e );
310 }
311
312 wxFileName fn( pcbContext()->GetCurrentFileName() );
313
314 if( aDocument.board_filename().compare( fn.GetFullName() ) != 0 )
315 {
316 ApiResponseStatus e;
317 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
318 e.set_error_message( fmt::format( "the requested document {} is not open",
319 aDocument.board_filename() ) );
320 return tl::unexpected( e );
321 }
322
323 return true;
324}
325
326
328{
329 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
330 return tl::unexpected( *busy );
331
332 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
333 {
334 ApiResponseStatus e;
335 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
336 e.set_status( ApiStatusCode::AS_UNHANDLED );
337 return tl::unexpected( e );
338 }
339
340 GetItemsResponse response;
341
342 BOARD* board = this->board();
343 std::vector<BOARD_ITEM*> items;
344 std::set<KICAD_T> typesRequested, typesInserted;
345 bool handledAnything = false;
346
347 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
348 {
349 typesRequested.emplace( type );
350
351 if( typesInserted.count( type ) )
352 continue;
353
354 switch( type )
355 {
356 case PCB_TRACE_T:
357 case PCB_ARC_T:
358 case PCB_VIA_T:
359 handledAnything = true;
360 std::copy( board->Tracks().begin(), board->Tracks().end(),
361 std::back_inserter( items ) );
362 typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
363 break;
364
365 case PCB_PAD_T:
366 {
367 handledAnything = true;
368
369 for( FOOTPRINT* fp : board->Footprints() )
370 {
371 std::copy( fp->Pads().begin(), fp->Pads().end(),
372 std::back_inserter( items ) );
373 }
374
375 typesInserted.insert( PCB_PAD_T );
376 break;
377 }
378
379 case PCB_FOOTPRINT_T:
380 {
381 handledAnything = true;
382
383 std::copy( board->Footprints().begin(), board->Footprints().end(),
384 std::back_inserter( items ) );
385
386 typesInserted.insert( PCB_FOOTPRINT_T );
387 break;
388 }
389
390 case PCB_SHAPE_T:
391 case PCB_TEXT_T:
392 case PCB_TEXTBOX_T:
393 case PCB_BARCODE_T:
395 {
396 handledAnything = true;
397 bool inserted = false;
398
399 for( BOARD_ITEM* item : board->Drawings() )
400 {
401 if( item->Type() == type )
402 {
403 items.emplace_back( item );
404 inserted = true;
405 }
406 }
407
408 if( inserted )
409 typesInserted.insert( type );
410
411 break;
412 }
413
414 case PCB_DIMENSION_T:
415 {
416 handledAnything = true;
417 bool inserted = false;
418
419 for( BOARD_ITEM* item : board->Drawings() )
420 {
421 switch (item->Type()) {
423 case PCB_DIM_CENTER_T:
424 case PCB_DIM_RADIAL_T:
426 case PCB_DIM_LEADER_T:
427 items.emplace_back( item );
428 inserted = true;
429 break;
430 default:
431 break;
432 }
433 }
434 // we have to add the dimension subtypes to the requested to get them out
436
437 if( inserted )
439
440 break;
441 }
442
443 case PCB_ZONE_T:
444 {
445 handledAnything = true;
446
447 std::copy( board->Zones().begin(), board->Zones().end(),
448 std::back_inserter( items ) );
449
450 typesInserted.insert( PCB_ZONE_T );
451 break;
452 }
453
454 case PCB_GROUP_T:
455 {
456 handledAnything = true;
457
458 std::copy( board->Groups().begin(), board->Groups().end(),
459 std::back_inserter( items ) );
460
461 typesInserted.insert( PCB_GROUP_T );
462 break;
463 }
464 default:
465 break;
466 }
467 }
468
469 if( !handledAnything )
470 {
471 ApiResponseStatus e;
472 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
473 e.set_error_message( "none of the requested types are valid for a Board object" );
474 return tl::unexpected( e );
475 }
476
477 for( const BOARD_ITEM* item : items )
478 {
479 if( !typesRequested.count( item->Type() ) )
480 continue;
481
482 google::protobuf::Any itemBuf;
483 item->Serialize( itemBuf );
484 response.mutable_items()->Add( std::move( itemBuf ) );
485 }
486
487 response.set_status( ItemRequestStatus::IRS_OK );
488 return response;
489}
490
491
494{
495 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
496
497 if( !documentValidation )
498 return tl::unexpected( documentValidation.error() );
499
500 if( aCtx.Request.copper_layer_count() % 2 != 0 )
501 {
502 ApiResponseStatus e;
503 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
504 e.set_error_message( "copper_layer_count must be an even number" );
505 return tl::unexpected( e );
506 }
507
508 if( aCtx.Request.copper_layer_count() > MAX_CU_LAYERS )
509 {
510 ApiResponseStatus e;
511 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
512 e.set_error_message( fmt::format( "copper_layer_count must be below %d", MAX_CU_LAYERS ) );
513 return tl::unexpected( e );
514 }
515
516 int copperLayerCount = static_cast<int>( aCtx.Request.copper_layer_count() );
517 LSET enabled = board::UnpackLayerSet( aCtx.Request.layers() );
518
519 // Sanitize the input
520 enabled |= LSET( { Edge_Cuts, Margin, F_CrtYd, B_CrtYd } );
521 enabled &= ~LSET::AllCuMask();
522 enabled |= LSET::AllCuMask( copperLayerCount );
523
524 BOARD* board = this->board();
525
526 LSET previousEnabled = board->GetEnabledLayers();
527 LSET changedLayers = enabled ^ previousEnabled;
528
529 board->SetEnabledLayers( enabled );
530 board->SetVisibleLayers( board->GetVisibleLayers() | changedLayers );
531
532 LSEQ removedLayers;
533
534 for( PCB_LAYER_ID layer_id : previousEnabled )
535 {
536 if( !enabled[layer_id] && board->HasItemsOnLayer( layer_id ) )
537 removedLayers.push_back( layer_id );
538 }
539
540 bool modified = false;
541
542 if( !removedLayers.empty() )
543 {
545
546 for( PCB_LAYER_ID layer_id : removedLayers )
547 modified |= board->RemoveAllItemsOnLayer( layer_id );
548 }
549
550 if( enabled != previousEnabled )
552
553 if( modified )
554 frame()->OnModify();
555
556 BoardEnabledLayersResponse response;
557
558 response.set_copper_layer_count( copperLayerCount );
559 board::PackLayerSet( *response.mutable_layers(), enabled );
560
561 return response;
562}
563
564
567{
568 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
569
570 if( !documentValidation )
571 return tl::unexpected( documentValidation.error() );
572
574 BoardDesignRulesResponse response;
575 kiapi::board::BoardDesignRules* rules = response.mutable_rules();
576
577 kiapi::board::MinimumConstraints* constraints = rules->mutable_constraints();
578
579 constraints->mutable_min_clearance()->set_value_nm( bds.m_MinClearance );
580 constraints->mutable_min_groove_width()->set_value_nm( bds.m_MinGrooveWidth );
581 constraints->mutable_min_connection_width()->set_value_nm( bds.m_MinConn );
582 constraints->mutable_min_track_width()->set_value_nm( bds.m_TrackMinWidth );
583 constraints->mutable_min_via_annular_width()->set_value_nm( bds.m_ViasMinAnnularWidth );
584 constraints->mutable_min_via_size()->set_value_nm( bds.m_ViasMinSize );
585 constraints->mutable_min_through_drill()->set_value_nm( bds.m_MinThroughDrill );
586 constraints->mutable_min_microvia_size()->set_value_nm( bds.m_MicroViasMinSize );
587 constraints->mutable_min_microvia_drill()->set_value_nm( bds.m_MicroViasMinDrill );
588 constraints->mutable_copper_edge_clearance()->set_value_nm( bds.m_CopperEdgeClearance );
589 constraints->mutable_hole_clearance()->set_value_nm( bds.m_HoleClearance );
590 constraints->mutable_hole_to_hole_min()->set_value_nm( bds.m_HoleToHoleMin );
591 constraints->mutable_silk_clearance()->set_value_nm( bds.m_SilkClearance );
592 constraints->set_min_resolved_spokes( bds.m_MinResolvedSpokes );
593 constraints->mutable_min_silk_text_height()->set_value_nm( bds.m_MinSilkTextHeight );
594 constraints->mutable_min_silk_text_thickness()->set_value_nm( bds.m_MinSilkTextThickness );
595
596 kiapi::board::PredefinedSizes* sizes = rules->mutable_predefined_sizes();
597
598 for( size_t ii = 1; ii < bds.m_TrackWidthList.size(); ++ii )
599 sizes->add_tracks()->mutable_width()->set_value_nm( bds.m_TrackWidthList[ii] );
600
601 for( size_t ii = 1; ii < bds.m_ViasDimensionsList.size(); ++ii )
602 {
603 kiapi::board::PresetViaDimension* via = sizes->add_vias();
604 via->mutable_diameter()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Diameter );
605 via->mutable_drill()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Drill );
606 }
607
608 for( size_t ii = 1; ii < bds.m_DiffPairDimensionsList.size(); ++ii )
609 {
610 kiapi::board::PresetDiffPairDimension* pair = sizes->add_diff_pairs();
611 pair->mutable_width()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Width );
612 pair->mutable_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Gap );
613 pair->mutable_via_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_ViaGap );
614 }
615
616 kiapi::board::SolderMaskPasteDefaults* maskPaste = rules->mutable_solder_mask_paste();
617
618 maskPaste->mutable_mask_expansion()->set_value_nm( bds.m_SolderMaskExpansion );
619 maskPaste->mutable_mask_min_width()->set_value_nm( bds.m_SolderMaskMinWidth );
620 maskPaste->mutable_mask_to_copper_clearance()->set_value_nm( bds.m_SolderMaskToCopperClearance );
621 maskPaste->mutable_paste_margin()->set_value_nm( bds.m_SolderPasteMargin );
622 maskPaste->set_paste_margin_ratio( bds.m_SolderPasteMarginRatio );
623 maskPaste->set_allow_soldermask_bridges_in_footprints( bds.m_AllowSoldermaskBridgesInFPs );
624
625 kiapi::board::TeardropDefaults* teardrops = rules->mutable_teardrops();
626
627 teardrops->set_target_vias( bds.m_TeardropParamsList.m_TargetVias );
628 teardrops->set_target_pth_pads( bds.m_TeardropParamsList.m_TargetPTHPads );
629 teardrops->set_target_smd_pads( bds.m_TeardropParamsList.m_TargetSMDPads );
630 teardrops->set_target_track_to_track( bds.m_TeardropParamsList.m_TargetTrack2Track );
631 teardrops->set_use_round_shapes_only( bds.m_TeardropParamsList.m_UseRoundShapesOnly );
632
634
635 for( int target = TARGET_ROUND; target <= TARGET_TRACK; ++target )
636 {
637 const TEARDROP_PARAMETERS* params = tdList.GetParameters( static_cast<TARGET_TD>( target ) );
638 kiapi::board::TeardropTargetEntry* entry = teardrops->add_target_params();
639
641 static_cast<TARGET_TD>( target ) ) );
642 entry->mutable_params()->set_enabled( params->m_Enabled );
643 entry->mutable_params()->mutable_max_length()->set_value_nm( params->m_TdMaxLen );
644 entry->mutable_params()->mutable_max_width()->set_value_nm( params->m_TdMaxWidth );
645 entry->mutable_params()->set_best_length_ratio( params->m_BestLengthRatio );
646 entry->mutable_params()->set_best_width_ratio( params->m_BestWidthRatio );
647 entry->mutable_params()->set_width_to_size_filter_ratio( params->m_WidthtoSizeFilterRatio );
648 entry->mutable_params()->set_curved_edges( params->m_CurvedEdges );
649 entry->mutable_params()->set_allow_two_tracks( params->m_AllowUseTwoTracks );
650 entry->mutable_params()->set_on_pads_in_zones( params->m_TdOnPadsInZones );
651 }
652
653 kiapi::board::ViaProtectionDefaults* viaProtection = rules->mutable_via_protection();
654
655 viaProtection->set_tent_front( bds.m_TentViasFront );
656 viaProtection->set_tent_back( bds.m_TentViasBack );
657 viaProtection->set_cover_front( bds.m_CoverViasFront );
658 viaProtection->set_cover_back( bds.m_CoverViasBack );
659 viaProtection->set_plug_front( bds.m_PlugViasFront );
660 viaProtection->set_plug_back( bds.m_PlugViasBack );
661 viaProtection->set_cap( bds.m_CapVias );
662 viaProtection->set_fill( bds.m_FillVias );
663
664 for( const auto& [errorCode, severity] : bds.m_DRCSeverities )
665 {
666 board::DrcSeveritySetting* setting = rules->add_severities();
667 setting->set_rule_type(
669 setting->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( severity ) );
670 }
671
672 for( const wxString& serialized : bds.m_DrcExclusions )
673 {
674 kiapi::board::DrcExclusion* exclusion = rules->add_exclusions();
675 exclusion->mutable_marker()->mutable_id()->set_opaque_id( serialized.ToStdString() );
676
677 auto it = bds.m_DrcExclusionComments.find( serialized );
678
679 if( it != bds.m_DrcExclusionComments.end() )
680 exclusion->set_comment( it->second.ToStdString() );
681 }
682
683 response.set_custom_rules_status( CRS_NONE );
684
685 wxString rulesPath = board()->GetDesignRulesPath();
686
687 if( !rulesPath.IsEmpty() && wxFileName::IsFileReadable( rulesPath ) )
688 {
689 wxFFile file( rulesPath, "r" );
690 wxString content;
691 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
692
693 if( !file.IsOpened() )
694 {
695 response.set_custom_rules_status( CRS_INVALID );
696 return response;
697 }
698
699 file.ReadAll( &content );
700 file.Close();
701
702 try
703 {
704 DRC_RULES_PARSER parser( content, "File" );
705 parser.Parse( parsedRules, nullptr );
706 response.set_custom_rules_status( CRS_VALID );
707 }
708 catch( const IO_ERROR& )
709 {
710 response.set_custom_rules_status( CRS_INVALID );
711 }
712 }
713
714 return response;
715}
716
717
720{
721 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
722
723 if( !documentValidation )
724 return tl::unexpected( documentValidation.error() );
725
726 BOARD_DESIGN_SETTINGS newSettings( board()->GetDesignSettings() );
727 const kiapi::board::BoardDesignRules& rules = aCtx.Request.rules();
728
729 if( rules.has_constraints() )
730 {
731 const kiapi::board::MinimumConstraints& constraints = rules.constraints();
732
733 newSettings.m_MinClearance = constraints.min_clearance().value_nm();
734 newSettings.m_MinGrooveWidth = constraints.min_groove_width().value_nm();
735 newSettings.m_MinConn = constraints.min_connection_width().value_nm();
736 newSettings.m_TrackMinWidth = constraints.min_track_width().value_nm();
737 newSettings.m_ViasMinAnnularWidth = constraints.min_via_annular_width().value_nm();
738 newSettings.m_ViasMinSize = constraints.min_via_size().value_nm();
739 newSettings.m_MinThroughDrill = constraints.min_through_drill().value_nm();
740 newSettings.m_MicroViasMinSize = constraints.min_microvia_size().value_nm();
741 newSettings.m_MicroViasMinDrill = constraints.min_microvia_drill().value_nm();
742 newSettings.m_CopperEdgeClearance = constraints.copper_edge_clearance().value_nm();
743 newSettings.m_HoleClearance = constraints.hole_clearance().value_nm();
744 newSettings.m_HoleToHoleMin = constraints.hole_to_hole_min().value_nm();
745 newSettings.m_SilkClearance = constraints.silk_clearance().value_nm();
746 newSettings.m_MinResolvedSpokes = constraints.min_resolved_spokes();
747 newSettings.m_MinSilkTextHeight = constraints.min_silk_text_height().value_nm();
748 newSettings.m_MinSilkTextThickness = constraints.min_silk_text_thickness().value_nm();
749 }
750
751 if( rules.has_predefined_sizes() )
752 {
753 newSettings.m_TrackWidthList.clear();
754 newSettings.m_TrackWidthList.emplace_back( 0 );
755
756 for( const kiapi::board::PresetTrackWidth& track : rules.predefined_sizes().tracks() )
757 newSettings.m_TrackWidthList.emplace_back( track.width().value_nm() );
758
759 newSettings.m_ViasDimensionsList.clear();
760 newSettings.m_ViasDimensionsList.emplace_back( 0, 0 );
761
762 for( const kiapi::board::PresetViaDimension& via : rules.predefined_sizes().vias() )
763 {
764 newSettings.m_ViasDimensionsList.emplace_back( static_cast<int>( via.diameter().value_nm() ),
765 static_cast<int>( via.drill().value_nm() ) );
766 }
767
768 newSettings.m_DiffPairDimensionsList.clear();
769 newSettings.m_DiffPairDimensionsList.emplace_back( 0, 0, 0 );
770
771 for( const kiapi::board::PresetDiffPairDimension& pair : rules.predefined_sizes().diff_pairs() )
772 {
773 newSettings.m_DiffPairDimensionsList.emplace_back(
774 static_cast<int>( pair.width().value_nm() ),
775 static_cast<int>( pair.gap().value_nm() ),
776 static_cast<int>( pair.via_gap().value_nm() ) );
777 }
778 }
779
780 if( rules.has_solder_mask_paste() )
781 {
782 const kiapi::board::SolderMaskPasteDefaults& maskPaste = rules.solder_mask_paste();
783
784 newSettings.m_SolderMaskExpansion = maskPaste.mask_expansion().value_nm();
785 newSettings.m_SolderMaskMinWidth = maskPaste.mask_min_width().value_nm();
786 newSettings.m_SolderMaskToCopperClearance = maskPaste.mask_to_copper_clearance().value_nm();
787 newSettings.m_SolderPasteMargin = maskPaste.paste_margin().value_nm();
788 newSettings.m_SolderPasteMarginRatio = maskPaste.paste_margin_ratio();
790 maskPaste.allow_soldermask_bridges_in_footprints();
791 }
792
793 if( rules.has_teardrops() )
794 {
795 const kiapi::board::TeardropDefaults& teardrops = rules.teardrops();
796
797 newSettings.m_TeardropParamsList.m_TargetVias = teardrops.target_vias();
798 newSettings.m_TeardropParamsList.m_TargetPTHPads = teardrops.target_pth_pads();
799 newSettings.m_TeardropParamsList.m_TargetSMDPads = teardrops.target_smd_pads();
800 newSettings.m_TeardropParamsList.m_TargetTrack2Track = teardrops.target_track_to_track();
801 newSettings.m_TeardropParamsList.m_UseRoundShapesOnly = teardrops.use_round_shapes_only();
802
803 for( const kiapi::board::TeardropTargetEntry& entry : teardrops.target_params() )
804 {
805 if( entry.target() == kiapi::board::TeardropTarget::TDT_UNKNOWN )
806 continue;
807
809 entry.target() );
810
811 TEARDROP_PARAMETERS* params = newSettings.m_TeardropParamsList.GetParameters( target );
812
813 params->m_Enabled = entry.params().enabled();
814 params->m_TdMaxLen = entry.params().max_length().value_nm();
815 params->m_TdMaxWidth = entry.params().max_width().value_nm();
816 params->m_BestLengthRatio = entry.params().best_length_ratio();
817 params->m_BestWidthRatio = entry.params().best_width_ratio();
818 params->m_WidthtoSizeFilterRatio = entry.params().width_to_size_filter_ratio();
819 params->m_CurvedEdges = entry.params().curved_edges();
820 params->m_AllowUseTwoTracks = entry.params().allow_two_tracks();
821 params->m_TdOnPadsInZones = entry.params().on_pads_in_zones();
822 }
823 }
824
825 if( rules.has_via_protection() )
826 {
827 const kiapi::board::ViaProtectionDefaults& viaProtection = rules.via_protection();
828
829 newSettings.m_TentViasFront = viaProtection.tent_front();
830 newSettings.m_TentViasBack = viaProtection.tent_back();
831 newSettings.m_CoverViasFront = viaProtection.cover_front();
832 newSettings.m_CoverViasBack = viaProtection.cover_back();
833 newSettings.m_PlugViasFront = viaProtection.plug_front();
834 newSettings.m_PlugViasBack = viaProtection.plug_back();
835 newSettings.m_CapVias = viaProtection.cap();
836 newSettings.m_FillVias = viaProtection.fill();
837 }
838
839 if( rules.severities_size() > 0 )
840 {
841 newSettings.m_DRCSeverities.clear();
842
843 for( const kiapi::board::DrcSeveritySetting& severitySetting : rules.severities() )
844 {
845 PCB_DRC_CODE ruleType =
846 FromProtoEnum<PCB_DRC_CODE, kiapi::board::DrcErrorType>( severitySetting.rule_type() );
847
848 const std::unordered_set<SEVERITY> permitted( { RPT_SEVERITY_ERROR, RPT_SEVERITY_WARNING, RPT_SEVERITY_IGNORE } );
849 SEVERITY setting = FromProtoEnum<SEVERITY, kiapi::common::types::RuleSeverity>( severitySetting.severity() );
850
851 if( !permitted.contains( setting ) )
852 {
853 ApiResponseStatus e;
854 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
855 e.set_error_message( fmt::format( "DRC severity must be error, warning, or ignore" ) );
856 return tl::unexpected( e );
857 }
858
859 newSettings.m_DRCSeverities[ruleType] = setting;
860 }
861 }
862
863 if( rules.exclusions_size() > 0 )
864 {
865 newSettings.m_DrcExclusions.clear();
866 newSettings.m_DrcExclusionComments.clear();
867
868 for( const kiapi::board::DrcExclusion& exclusion : rules.exclusions() )
869 {
870 wxString serialized = wxString::FromUTF8( exclusion.marker().id().opaque_id() );
871
872 if( serialized.IsEmpty() )
873 {
874 ApiResponseStatus e;
875 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
876 e.set_error_message( "DrcExclusion marker id must not be empty" );
877 return tl::unexpected( e );
878 }
879
880 newSettings.m_DrcExclusions.insert( serialized );
881 newSettings.m_DrcExclusionComments[serialized] = wxString::FromUTF8( exclusion.comment() );
882 }
883 }
884
885 std::vector<BOARD_DESIGN_SETTINGS::VALIDATION_ERROR> errors = newSettings.ValidateDesignRules();
886
887 if( !errors.empty() )
888 {
889 const BOARD_DESIGN_SETTINGS::VALIDATION_ERROR& error = errors.front();
890
891 ApiResponseStatus e;
892 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
893 e.set_error_message( fmt::format( "Invalid board design rules: {}: {}",
894 error.setting_name.ToStdString(),
895 error.error_message.ToStdString() ) );
896 return tl::unexpected( e );
897 }
898
899 board()->SetDesignSettings( newSettings );
900
901 if( frame() )
902 {
903 frame()->OnModify();
905 }
906
907 HANDLER_CONTEXT<GetBoardDesignRules> getCtx = { aCtx.ClientName, GetBoardDesignRules() };
908 *getCtx.Request.mutable_board() = aCtx.Request.board();
909
910 return handleGetBoardDesignRules( getCtx );
911}
912
913
916{
917 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
918
919 if( !documentValidation )
920 return tl::unexpected( documentValidation.error() );
921
922 CustomRulesResponse response;
923 response.set_status( CRS_NONE );
924
925 wxString rulesPath = board()->GetDesignRulesPath();
926
927 if( rulesPath.IsEmpty() || !wxFileName::IsFileReadable( rulesPath ) )
928 return response;
929
930 wxFFile file( rulesPath, "r" );
931
932 if( !file.IsOpened() )
933 {
934 response.set_status( CRS_INVALID );
935 response.set_error_text( "Failed to open custom rules file" );
936 return response;
937 }
938
939 wxString content;
940 file.ReadAll( &content );
941 file.Close();
942
943 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
944
945 try
946 {
947 DRC_RULES_PARSER parser( content, "File" );
948 parser.Parse( parsedRules, nullptr );
949 }
950 catch( const IO_ERROR& ioe )
951 {
952 response.set_status( CRS_INVALID );
953 response.set_error_text( ioe.What().ToStdString() );
954 return response;
955 }
956
957 for( const std::shared_ptr<DRC_RULE>& rule : parsedRules )
958 {
959 // TODO(JE) since we now need this for both here and the rules editor, maybe it's time
960 // to just make comment parsing part of the parser?
961 wxString text = DRC_RULE_LOADER::ExtractRuleText( content, rule->m_Name );
962 wxString comment = DRC_RULE_LOADER::ExtractRuleComment( text );
963
964 kiapi::board::CustomRule* customRule = response.add_rules();
965
966 if( rule->m_Condition )
967 customRule->set_condition( rule->m_Condition->GetExpression().ToUTF8() );
968
969 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
970 {
971 board::CustomRuleConstraint* constraintProto = customRule->add_constraints();
972 constraint.ToProto( *constraintProto );
973 }
974
975 customRule->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( rule->m_Severity ) );
976 customRule->set_name( rule->m_Name.ToUTF8() );
977
978 if( rule->m_LayerSource.CmpNoCase( wxS( "outer" ) ) == 0 )
979 {
980 customRule->set_layer_mode( kiapi::board::CRLM_OUTER );
981 }
982 else if( rule->m_LayerSource.CmpNoCase( wxS( "inner" ) ) == 0 )
983 {
984 customRule->set_layer_mode( kiapi::board::CRLM_INNER );
985 }
986 else if( !rule->m_LayerSource.IsEmpty() )
987 {
988 int layer = LSET::NameToLayer( rule->m_LayerSource );
989
990 if( layer != UNDEFINED_LAYER && layer != UNSELECTED_LAYER && layer < PCB_LAYER_ID_COUNT )
991 {
992 customRule->set_single_layer(
994 }
995 }
996
997 if( !comment.IsEmpty() )
998 customRule->set_comments( comment );
999 }
1000
1001 response.set_status( CRS_VALID );
1002 return response;
1003}
1004
1005
1008{
1009 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1010
1011 if( !documentValidation )
1012 return tl::unexpected( documentValidation.error() );
1013
1014 wxString rulesPath = board()->GetDesignRulesPath();
1015
1016 if( aCtx.Request.rules_size() == 0 )
1017 {
1018 if( wxFileName::FileExists( rulesPath ) )
1019 {
1020 if( !wxRemoveFile( rulesPath ) )
1021 {
1022 CustomRulesResponse response;
1023 response.set_status( CRS_INVALID );
1024 response.set_error_text( "Failed to remove custom rules file" );
1025 return response;
1026 }
1027 }
1028
1029 CustomRulesResponse response;
1030 response.set_status( CRS_NONE );
1031 return response;
1032 }
1033
1034 wxString rulesText;
1035 rulesText << "(version 2)\n";
1036
1037 for( const board::CustomRule& rule : aCtx.Request.rules() )
1038 {
1039 wxString serializationError;
1040 wxString serializedRule = DRC_RULE::FormatRuleFromProto( rule, &serializationError );
1041
1042 if( serializedRule.IsEmpty() )
1043 {
1044 CustomRulesResponse response;
1045 response.set_status( CRS_INVALID );
1046
1047 if( serializationError.IsEmpty() )
1048 response.set_error_text( "Failed to serialize custom rule" );
1049 else
1050 response.set_error_text( serializationError.ToUTF8() );
1051
1052 return response;
1053 }
1054
1055 rulesText << "\n" << serializedRule;
1056 }
1057
1058 // Validate generated file text before writing so callers get parser errors in response.
1059 try
1060 {
1061 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1062 DRC_RULES_PARSER parser( rulesText, "SetCustomDesignRules" );
1063 parser.Parse( parsedRules, nullptr );
1064 }
1065 catch( const IO_ERROR& ioe )
1066 {
1067 CustomRulesResponse response;
1068 response.set_status( CRS_INVALID );
1069 response.set_error_text( ioe.What().ToStdString() );
1070 return response;
1071 }
1072
1073 wxFFile file( rulesPath, "w" );
1074
1075 if( !file.IsOpened() )
1076 {
1077 CustomRulesResponse response;
1078 response.set_status( CRS_INVALID );
1079 response.set_error_text( "Failed to open custom rules file for writing" );
1080 return response;
1081 }
1082
1083 if( !file.Write( rulesText ) )
1084 {
1085 file.Close();
1086
1087 CustomRulesResponse response;
1088 response.set_status( CRS_INVALID );
1089 response.set_error_text( "Failed to write custom rules file" );
1090 return response;
1091 }
1092
1093 file.Close();
1094
1095 HANDLER_CONTEXT<GetCustomDesignRules> getCtx = { aCtx.ClientName, GetCustomDesignRules() };
1096 *getCtx.Request.mutable_board() = aCtx.Request.board();
1097 return handleGetCustomDesignRules( getCtx );
1098}
1099
1100
1103{
1104 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1105 !documentValidation )
1106 {
1107 return tl::unexpected( documentValidation.error() );
1108 }
1109
1110 VECTOR2I origin;
1111 const BOARD_DESIGN_SETTINGS& settings = board()->GetDesignSettings();
1112
1113 switch( aCtx.Request.type() )
1114 {
1115 case BOT_GRID:
1116 origin = settings.GetGridOrigin();
1117 break;
1118
1119 case BOT_DRILL:
1120 origin = settings.GetAuxOrigin();
1121 break;
1122
1123 default:
1124 case BOT_UNKNOWN:
1125 {
1126 ApiResponseStatus e;
1127 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1128 e.set_error_message( "Unexpected origin type" );
1129 return tl::unexpected( e );
1130 }
1131 }
1132
1133 types::Vector2 reply;
1134 PackVector2( reply, origin );
1135 return reply;
1136}
1137
1140{
1141 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1142 return tl::unexpected( *busy );
1143
1144 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1145 !documentValidation )
1146 {
1147 return tl::unexpected( documentValidation.error() );
1148 }
1149
1150 VECTOR2I origin = UnpackVector2( aCtx.Request.origin() );
1151
1152 switch( aCtx.Request.type() )
1153 {
1154 case BOT_GRID:
1155 {
1156 PCB_EDIT_FRAME* f = frame();
1157
1158 frame()->CallAfter( [f, origin]()
1159 {
1160 // gridSetOrigin takes ownership and frees this
1161 VECTOR2D* dorigin = new VECTOR2D( origin );
1162 TOOL_MANAGER* mgr = f->GetToolManager();
1163 mgr->RunAction( PCB_ACTIONS::gridSetOrigin, dorigin );
1164 f->Refresh();
1165 } );
1166 break;
1167 }
1168
1169 case BOT_DRILL:
1170 {
1171 PCB_EDIT_FRAME* f = frame();
1172
1173 frame()->CallAfter( [f, origin]()
1174 {
1175 TOOL_MANAGER* mgr = f->GetToolManager();
1176 mgr->RunAction( PCB_ACTIONS::drillSetOrigin, origin );
1177 f->Refresh();
1178 } );
1179 break;
1180 }
1181
1182 default:
1183 case BOT_UNKNOWN:
1184 {
1185 ApiResponseStatus e;
1186 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1187 e.set_error_message( "Unexpected origin type" );
1188 return tl::unexpected( e );
1189 }
1190 }
1191
1192 return Empty();
1193}
1194
1195
1198{
1199 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1200 !documentValidation )
1201 {
1202 return tl::unexpected( documentValidation.error() );
1203 }
1204
1205 BoardLayerNameResponse response;
1206
1208
1209 response.set_name( board()->GetLayerName( id ) );
1210
1211 return response;
1212}
1213
1214
1215std::optional<TITLE_BLOCK*> API_HANDLER_PCB::getTitleBlock()
1216{
1217 return &context()->GetBoard()->GetTitleBlock();
1218}
1219
1220
1221std::optional<PAGE_INFO> API_HANDLER_PCB::getPageSettings()
1222{
1223 return context()->GetBoard()->GetPageSettings();
1224}
1225
1226
1228{
1229 context()->GetBoard()->SetPageSettings( aPageInfo );
1230 return true;
1231}
1232
1233
1238
1239
1240void API_HANDLER_PCB::setDrawingSheetFileName( const wxString& aFileName )
1241{
1243
1244 if( frame() )
1246}
1247
1248
1250{
1251 if( frame() )
1252 {
1253 frame()->Refresh();
1254 frame()->OnModify();
1256 }
1257}
1258
1259
1261{
1262 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1263
1264 if( !documentValidation )
1265 return tl::unexpected( documentValidation.error() );
1266
1267 NetsResponse response;
1268 BOARD* board = this->board();
1269
1270 std::set<wxString> netclassFilter;
1271
1272 for( const std::string& nc : aCtx.Request.netclass_filter() )
1273 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
1274
1275 for( NETINFO_ITEM* net : board->GetNetInfo() )
1276 {
1277 NETCLASS* nc = net->GetNetClass();
1278
1279 if( !netclassFilter.empty() && nc )
1280 {
1281 bool inClass = false;
1282
1283 for( const wxString& filter : netclassFilter )
1284 {
1285 if( nc->ContainsNetclassWithName( filter ) )
1286 {
1287 inClass = true;
1288 break;
1289 }
1290 }
1291
1292 if( !inClass )
1293 continue;
1294 }
1295
1296 board::types::Net* netProto = response.add_nets();
1297 netProto->set_name( net->GetNetname() );
1298 netProto->mutable_code()->set_value( net->GetNetCode() );
1299 }
1300
1301 return response;
1302}
1303
1304
1307{
1308 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1309 return tl::unexpected( *busy );
1310
1311 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1312 {
1313 ApiResponseStatus e;
1314 e.set_status( ApiStatusCode::AS_UNHANDLED );
1315 return tl::unexpected( e );
1316 }
1317
1318 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1319 const bool filterByType = aCtx.Request.types_size() > 0;
1320
1321 if( filterByType && types.empty() )
1322 {
1323 ApiResponseStatus e;
1324 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1325 e.set_error_message( "none of the requested types are valid for a Board object" );
1326 return tl::unexpected( e );
1327 }
1328
1329 std::set<KICAD_T> typeFilter( types.begin(), types.end() );
1330 std::vector<BOARD_CONNECTED_ITEM*> sourceItems;
1331
1332 for( const types::KIID& id : aCtx.Request.items() )
1333 {
1334 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
1335 {
1336 if( BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( *item ) )
1337 sourceItems.emplace_back( connected );
1338 }
1339 }
1340
1341 if( sourceItems.empty() )
1342 {
1343 ApiResponseStatus e;
1344 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1345 e.set_error_message( "none of the requested IDs were found or valid connected items" );
1346 return tl::unexpected( e );
1347 }
1348
1349 GetItemsResponse response;
1350 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
1351 std::set<KIID> insertedItems;
1352
1353 for( BOARD_CONNECTED_ITEM* source : sourceItems )
1354 {
1355 for( BOARD_CONNECTED_ITEM* connected : conn->GetConnectedItems( source ) )
1356 {
1357 if( filterByType && !typeFilter.contains( connected->Type() ) )
1358 continue;
1359
1360 if( !insertedItems.insert( connected->m_Uuid ).second )
1361 continue;
1362
1363 connected->Serialize( *response.add_items() );
1364 }
1365 }
1366
1367 response.set_status( ItemRequestStatus::IRS_OK );
1368 return response;
1369}
1370
1371
1373 const HANDLER_CONTEXT<GetItemsByNet>& aCtx )
1374{
1375 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1376 return tl::unexpected( *busy );
1377
1378 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1379 {
1380 ApiResponseStatus e;
1381 e.set_status( ApiStatusCode::AS_UNHANDLED );
1382 return tl::unexpected( e );
1383 }
1384
1385 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1386 const bool filterByType = aCtx.Request.types_size() > 0;
1387
1388 if( filterByType && types.empty() )
1389 {
1390 ApiResponseStatus e;
1391 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1392 e.set_error_message( "none of the requested types are valid for a Board object" );
1393 return tl::unexpected( e );
1394 }
1395
1396 if( !filterByType )
1398
1399 GetItemsResponse response;
1400 BOARD* board = this->board();
1401 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
1402 std::set<KIID> insertedItems;
1403
1404 const NETINFO_LIST& nets = board->GetNetInfo();
1405
1406 for( const board::types::Net& net : aCtx.Request.nets() )
1407 {
1408 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
1409
1410 if( !netInfo )
1411 continue;
1412
1413 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( netInfo->GetNetCode(), types ) )
1414 {
1415 if( !insertedItems.insert( item->m_Uuid ).second )
1416 continue;
1417
1418 item->Serialize( *response.add_items() );
1419 }
1420 }
1421
1422 response.set_status( ItemRequestStatus::IRS_OK );
1423 return response;
1424}
1425
1426
1429{
1430 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1431 return tl::unexpected( *busy );
1432
1433 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1434 {
1435 ApiResponseStatus e;
1436 e.set_status( ApiStatusCode::AS_UNHANDLED );
1437 return tl::unexpected( e );
1438 }
1439
1440 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1441 const bool filterByType = aCtx.Request.types_size() > 0;
1442
1443 if( filterByType && types.empty() )
1444 {
1445 ApiResponseStatus e;
1446 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1447 e.set_error_message( "none of the requested types are valid for a Board object" );
1448 return tl::unexpected( e );
1449 }
1450
1451 if( !filterByType )
1453
1454 std::set<wxString> requestedClasses;
1455
1456 for( const std::string& netClass : aCtx.Request.net_classes() )
1457 requestedClasses.insert( wxString( netClass.c_str(), wxConvUTF8 ) );
1458
1459 GetItemsResponse response;
1460 BOARD* board = this->board();
1461 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
1462 std::set<KIID> insertedItems;
1463
1464 for( NETINFO_ITEM* net : board->GetNetInfo() )
1465 {
1466 if( !net )
1467 continue;
1468
1469 NETCLASS* nc = net->GetNetClass();
1470
1471 if( !requestedClasses.empty() )
1472 {
1473 if( !nc )
1474 continue;
1475
1476 bool inClass = false;
1477
1478 for( const wxString& filter : requestedClasses )
1479 {
1480 if( nc->ContainsNetclassWithName( filter ) )
1481 {
1482 inClass = true;
1483 break;
1484 }
1485 }
1486
1487 if( !inClass )
1488 continue;
1489 }
1490
1491 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( net->GetNetCode(), types ) )
1492 {
1493 if( !insertedItems.insert( item->m_Uuid ).second )
1494 continue;
1495
1496 item->Serialize( *response.add_items() );
1497 }
1498 }
1499
1500 response.set_status( ItemRequestStatus::IRS_OK );
1501 return response;
1502}
1503
1504
1507{
1508 NetClassForNetsResponse response;
1509
1510 BOARD* board = this->board();
1511 const NETINFO_LIST& nets = board->GetNetInfo();
1512 google::protobuf::Any any;
1513
1514 for( const board::types::Net& net : aCtx.Request.net() )
1515 {
1516 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
1517
1518 if( !netInfo )
1519 continue;
1520
1521 netInfo->GetNetClass()->Serialize( any );
1522 auto [pair, rc] = response.mutable_classes()->insert( { net.name(), {} } );
1523 any.UnpackTo( &pair->second );
1524 }
1525
1526 return response;
1527}
1528
1529
1531{
1532 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1533 return tl::unexpected( *busy );
1534
1535 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1536
1537 if( !documentValidation )
1538 return tl::unexpected( documentValidation.error() );
1539
1540 TOOL_MANAGER* mgr = toolManager();
1541
1542 // A frame's tool manager always carries the zone filler tool; headless sessions start with a
1543 // bare tool manager and register it on first use, like the CLI jobs do.
1544 if( !mgr->FindTool( ZONE_FILLER_TOOL_NAME ) )
1545 mgr->RegisterTool( new ZONE_FILLER_TOOL );
1546
1547 if( aCtx.Request.zones().empty() )
1548 {
1549 if( frame() )
1550 {
1551 frame()->CallAfter( [mgr]()
1552 {
1554 } );
1555 }
1556 else
1557 {
1558 // Headless sessions have no event loop to defer to; fill synchronously through the
1559 // same tool the CLI jobs use.
1560 mgr->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, nullptr, true );
1561 }
1562 }
1563 else
1564 {
1565 std::vector<ZONE*> toFill;
1566
1567 for( const types::KIID& id : aCtx.Request.zones() )
1568 {
1569 std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) );
1570
1571 if( !item || ( *item )->Type() != PCB_ZONE_T )
1572 {
1573 ApiResponseStatus e;
1574 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1575 e.set_error_message( fmt::format( "zone with ID {} not found on the board", id.value() ) );
1576 return tl::unexpected( e );
1577 }
1578
1579 ZONE* zone = static_cast<ZONE*>( *item );
1580
1581 // The filler silently skips rule areas, which would turn this into a false success
1582 if( zone->GetIsRuleArea() )
1583 {
1584 ApiResponseStatus e;
1585 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1586 e.set_error_message( fmt::format( "zone with ID {} is a rule area and cannot be filled",
1587 id.value() ) );
1588 return tl::unexpected( e );
1589 }
1590
1591 // A repeated id would enqueue concurrent fill tasks for the same zone
1592 if( !alg::contains( toFill, zone ) )
1593 toFill.push_back( zone );
1594 }
1595
1596 std::unique_ptr<COMMIT> commit = createCommit();
1597 ZONE_FILLER filler( board(), commit.get() );
1598
1599 if( !filler.Fill( toFill ) )
1600 {
1601 commit->Revert();
1602
1603 ApiResponseStatus e;
1604 e.set_status( ApiStatusCode::AS_UNKNOWN );
1605 e.set_error_message( "zone fill failed" );
1606 return tl::unexpected( e );
1607 }
1608
1609 commit->Push( _( "Fill Zone(s)" ), SKIP_CONNECTIVITY | ZONE_FILL_OP );
1610
1611 // Push skipped connectivity, so run the same post-fill refresh as the interactive fill
1612 mgr->GetTool<ZONE_FILLER_TOOL>()->PostFillRefresh( frame() == nullptr );
1613 }
1614
1615 return Empty();
1616}
1617
1618
1620{
1621 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1622 return tl::unexpected( *busy );
1623
1624 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1625
1626 if( !documentValidation )
1627 return tl::unexpected( documentValidation.error() );
1628
1629 wxFileName netlistPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.netlist_path() ) ) );
1630
1631 if( !netlistPath.IsOk() || !netlistPath.FileExists() )
1632 {
1633 ApiResponseStatus e;
1634 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1635 e.set_error_message(
1636 fmt::format( "netlist file '{}' could not be opened", netlistPath.GetFullPath().ToStdString() ) );
1637 return tl::unexpected( e );
1638 }
1639
1640 PCB_CONTEXT* ctx = pcbContext();
1642
1643 const bool lookupByTimestamp = aCtx.Request.match_mode() != NetlistMatchMode::NMM_REFERENCE;
1644
1646 netlist.SetFindByTimeStamp( lookupByTimestamp );
1647 netlist.SetReplaceFootprints( aCtx.Request.update_footprints() );
1648
1649 if( !ctx->ReadNetlistFromFile( netlistPath.GetFullPath(), netlist, reporter ) )
1650 {
1651 ApiResponseStatus e;
1652 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1653 e.set_error_message( fmt::format( "unable to handle netlist file '{}': {}",
1654 netlistPath.GetFullPath().ToStdString(),
1655 reporter.GetMessages().ToStdString() ) );
1656 return tl::unexpected( e );
1657 }
1658
1659 std::unique_ptr<BOARD_NETLIST_UPDATER> updater = ctx->MakeNetlistUpdater();
1660
1661 updater->SetReporter( &reporter );
1662 updater->SetIsDryRun( aCtx.Request.dry_run() );
1663 updater->SetLookupByTimestamp( lookupByTimestamp );
1664 updater->SetDeleteUnusedFootprints( aCtx.Request.delete_extra_footprints() );
1665 updater->SetReplaceFootprints( aCtx.Request.update_footprints() );
1666 updater->SetTransferGroups( aCtx.Request.transfer_groups() );
1667 updater->SetOverrideLocks( aCtx.Request.override_locks() );
1668 updater->SetUpdateFields( true );
1669
1670 const bool success = updater->UpdateNetlist( netlist );
1671
1672 if( !aCtx.Request.dry_run() && success )
1673 ctx->OnNetlistChanged( *updater );
1674
1675 ImportNetlistResponse response;
1676 response.set_report( reporter.GetMessages().ToUTF8() );
1677 response.set_error_count( updater->GetErrorCount() );
1678 response.set_warning_count( updater->GetWarningCount() );
1679 response.set_new_footprint_count( updater->GetNewFootprintCount() );
1680 return response;
1681}
1682
1683
1686{
1687 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetBoardEditorAppearanceSettings" ) )
1688 return tl::unexpected( *headless );
1689
1690 BoardEditorAppearanceSettings reply;
1691
1692 // TODO: might be nice to put all these things in one place and have it derive SERIALIZABLE
1693
1694 const PCB_DISPLAY_OPTIONS& displayOptions = frame()->GetDisplayOptions();
1695
1696 reply.set_inactive_layer_display( ToProtoEnum<HIGH_CONTRAST_MODE, InactiveLayerDisplayMode>(
1697 displayOptions.m_ContrastModeDisplay ) );
1698 reply.set_net_color_display(
1700
1701 reply.set_board_flip( frame()->GetCanvas()->GetView()->IsMirroredX()
1702 ? BoardFlipMode::BFM_FLIPPED_X
1703 : BoardFlipMode::BFM_NORMAL );
1704
1705 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
1706
1707 reply.set_ratsnest_display( ToProtoEnum<RATSNEST_MODE, RatsnestDisplayMode>(
1708 editorSettings->m_Display.m_RatsnestMode ) );
1709
1710 return reply;
1711}
1712
1713
1716{
1717 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetBoardEditorAppearanceSettings" ) )
1718 return tl::unexpected( *headless );
1719
1720 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1721 return tl::unexpected( *busy );
1722
1724 KIGFX::PCB_VIEW* view = frame()->GetCanvas()->GetView();
1725 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
1726 const BoardEditorAppearanceSettings& newSettings = aCtx.Request.settings();
1727
1728 options.m_ContrastModeDisplay =
1729 FromProtoEnum<HIGH_CONTRAST_MODE>( newSettings.inactive_layer_display() );
1730 options.m_NetColorMode =
1731 FromProtoEnum<NET_COLOR_MODE>( newSettings.net_color_display() );
1732
1733 bool flip = newSettings.board_flip() == BoardFlipMode::BFM_FLIPPED_X;
1734
1735 if( flip != view->IsMirroredX() )
1736 {
1737 view->SetMirror( !view->IsMirroredX(), view->IsMirroredY() );
1738 view->RecacheAllItems();
1739 }
1740
1741 editorSettings->m_Display.m_RatsnestMode =
1742 FromProtoEnum<RATSNEST_MODE>( newSettings.ratsnest_display() );
1743
1744 frame()->SetDisplayOptions( options );
1746 frame()->GetCanvas()->Refresh();
1747
1748 return Empty();
1749}
1750
1751
1754{
1755 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1756 return tl::unexpected( *busy );
1757
1758 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1759
1760 if( !documentValidation )
1761 return tl::unexpected( documentValidation.error() );
1762
1763 SEVERITY severity = FromProtoEnum<SEVERITY>( aCtx.Request.severity() );
1764 int layer = severity == RPT_SEVERITY_WARNING ? LAYER_DRC_WARNING : LAYER_DRC_ERROR;
1766
1767 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( code );
1768
1769 drcItem->SetErrorMessage( wxString::FromUTF8( aCtx.Request.message() ) );
1770
1771 RC_ITEM::KIIDS ids;
1772
1773 for( const auto& id : aCtx.Request.items() )
1774 ids.emplace_back( KIID( id.value() ) );
1775
1776 if( !ids.empty() )
1777 drcItem->SetItems( ids );
1778
1779 const auto& pos = aCtx.Request.position();
1780 VECTOR2I position( static_cast<int>( pos.x_nm() ), static_cast<int>( pos.y_nm() ) );
1781
1782 PCB_MARKER* marker = new PCB_MARKER( drcItem, position, layer );
1783
1784 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
1785 commit->Add( marker );
1786 commit->Push( wxS( "API injected DRC marker" ) );
1787
1788 InjectDrcErrorResponse response;
1789 response.mutable_marker()->set_value( marker->GetUUID().AsStdString() );
1790
1791 return response;
1792}
1793
1794
1795std::optional<ApiResponseStatus> ValidateUnitsInchMm( types::Units aUnits,
1796 const std::string& aCommandName )
1797{
1798 if( aUnits == types::Units::U_INCH || aUnits == types::Units::U_MM
1799 || aUnits == types::Units::U_UNKNOWN )
1800 {
1801 return std::nullopt;
1802 }
1803
1804 ApiResponseStatus e;
1805 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1806 e.set_error_message( fmt::format( "{} supports only inch and mm units", aCommandName ) );
1807 return e;
1808}
1809
1810
1811std::optional<ApiResponseStatus>
1812ValidatePaginationModeForSingleOrPerFile( kiapi::board::jobs::BoardJobPaginationMode aMode,
1813 const std::string& aCommandName )
1814{
1815 if( aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_UNKNOWN
1816 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_ALL_LAYERS_ONE_PAGE
1817 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_EACH_LAYER_OWN_FILE )
1818 {
1819 return std::nullopt;
1820 }
1821
1822 ApiResponseStatus e;
1823 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1824 e.set_error_message( fmt::format( "{} does not support EACH_LAYER_OWN_PAGE pagination mode",
1825 aCommandName ) );
1826 return e;
1827}
1828
1829
1830std::optional<ApiResponseStatus> ApplyBoardPlotSettings( const BoardPlotSettings& aSettings,
1831 JOB_EXPORT_PCB_PLOT& aJob )
1832{
1833 for( int layer : aSettings.layers() )
1834 {
1836 static_cast<board::types::BoardLayer>( layer ) );
1837
1838 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
1839 {
1840 ApiResponseStatus e;
1841 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1842 e.set_error_message( "Board plot settings contain an invalid layer" );
1843 return e;
1844 }
1845
1846 aJob.m_plotLayerSequence.push_back( layerId );
1847 }
1848
1849 for( int layer : aSettings.common_layers() )
1850 {
1852 static_cast<board::types::BoardLayer>( layer ) );
1853
1854 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
1855 {
1856 ApiResponseStatus e;
1857 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1858 e.set_error_message( "Board plot settings contain an invalid common layer" );
1859 return e;
1860 }
1861
1862 aJob.m_plotOnAllLayersSequence.push_back( layerId );
1863 }
1864
1865 aJob.m_colorTheme = wxString::FromUTF8( aSettings.color_theme() );
1866 aJob.m_drawingSheet = wxString::FromUTF8( aSettings.drawing_sheet() );
1867 aJob.m_variant = wxString::FromUTF8( aSettings.variant() );
1868
1869 aJob.m_mirror = aSettings.mirror();
1870 aJob.m_blackAndWhite = aSettings.black_and_white();
1871 aJob.m_negative = aSettings.negative();
1872 aJob.m_scale = aSettings.scale();
1873
1874 aJob.m_sketchPadsOnFabLayers = aSettings.sketch_pads_on_fab_layers();
1875 aJob.m_hideDNPFPsOnFabLayers = aSettings.hide_dnp_footprints_on_fab_layers();
1876 aJob.m_sketchDNPFPsOnFabLayers = aSettings.sketch_dnp_footprints_on_fab_layers();
1877 aJob.m_crossoutDNPFPsOnFabLayers = aSettings.crossout_dnp_footprints_on_fab_layers();
1878
1879 aJob.m_plotFootprintValues = aSettings.plot_footprint_values();
1880 aJob.m_plotRefDes = aSettings.plot_reference_designators();
1881 aJob.m_plotDrawingSheet = aSettings.plot_drawing_sheet();
1882 aJob.m_subtractSolderMaskFromSilk = aSettings.subtract_solder_mask_from_silk();
1883 aJob.m_plotPadNumbers = aSettings.plot_pad_numbers();
1884
1885 aJob.m_drillShapeOption = FromProtoEnum<DRILL_MARKS>( aSettings.drill_marks() );
1886
1887 aJob.m_useDrillOrigin = aSettings.use_drill_origin();
1888 aJob.m_checkZonesBeforePlot = aSettings.check_zones_before_plot();
1889
1890 return std::nullopt;
1891}
1892
1893
1895{
1896 types::RunJobResponse response;
1898
1899 if( !aContext || !aContext->GetKiway() )
1900 {
1901 response.set_status( types::JobStatus::JS_ERROR );
1902 response.set_message( "Internal error" );
1903 wxCHECK_MSG( false, response, "context missing valid kiway in ExecuteBoardJob?" );
1904 return response;
1905 }
1906
1907 int exitCode = aContext->GetKiway()->ProcessJob( KIWAY::FACE_PCB, &aJob, &reporter );
1908
1909 for( const JOB_OUTPUT& output : aJob.GetOutputs() )
1910 response.add_output_path( output.m_outputPath.ToUTF8() );
1911
1912 if( exitCode == 0 )
1913 {
1914 response.set_status( types::JobStatus::JS_SUCCESS );
1915 return response;
1916 }
1917
1918 response.set_status( types::JobStatus::JS_ERROR );
1919 response.set_message( fmt::format( "Board export job '{}' failed with exit code {}: {}",
1920 aJob.GetType(), exitCode,
1921 reporter.GetMessages().ToStdString() ) );
1922 return response;
1923}
1924
1925
1928{
1929 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1930 return tl::unexpected( *busy );
1931
1932 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1933
1934 if( !documentValidation )
1935 return tl::unexpected( documentValidation.error() );
1936
1939 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1940
1942
1943 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
1944 job.m_3dparams.m_NetFilter = wxString::FromUTF8( aCtx.Request.net_filter() );
1945 job.m_3dparams.m_ComponentFilter = wxString::FromUTF8( aCtx.Request.component_filter() );
1946
1947 job.m_hasUserOrigin = aCtx.Request.has_user_origin();
1948 job.m_3dparams.m_Origin = VECTOR2D( aCtx.Request.origin().x_nm(), aCtx.Request.origin().y_nm() );
1949
1950 job.m_3dparams.m_Overwrite = aCtx.Request.overwrite();
1951 job.m_3dparams.m_UseGridOrigin = aCtx.Request.use_grid_origin();
1952 job.m_3dparams.m_UseDrillOrigin = aCtx.Request.use_drill_origin();
1953 job.m_3dparams.m_UseDefinedOrigin = aCtx.Request.use_defined_origin() || aCtx.Request.has_user_origin();
1954 job.m_3dparams.m_UsePcbCenterOrigin = aCtx.Request.use_pcb_center_origin();
1955
1956 job.m_3dparams.m_IncludeUnspecified = aCtx.Request.include_unspecified();
1957 job.m_3dparams.m_IncludeDNP = aCtx.Request.include_dnp();
1958 job.m_3dparams.m_SubstModels = aCtx.Request.substitute_models();
1959
1960 job.m_3dparams.m_BoardOutlinesChainingEpsilon = aCtx.Request.board_outlines_chaining_epsilon();
1961 job.m_3dparams.m_BoardOnly = aCtx.Request.board_only();
1962 job.m_3dparams.m_CutViasInBody = aCtx.Request.cut_vias_in_body();
1963 job.m_3dparams.m_ExportBoardBody = aCtx.Request.export_board_body();
1964 job.m_3dparams.m_ExportComponents = aCtx.Request.export_components();
1965 job.m_3dparams.m_ExportTracksVias = aCtx.Request.export_tracks_and_vias();
1966 job.m_3dparams.m_ExportPads = aCtx.Request.export_pads();
1967 job.m_3dparams.m_ExportZones = aCtx.Request.export_zones();
1968 job.m_3dparams.m_ExportInnerCopper = aCtx.Request.export_inner_copper();
1969 job.m_3dparams.m_ExportSilkscreen = aCtx.Request.export_silkscreen();
1970 job.m_3dparams.m_ExportSoldermask = aCtx.Request.export_soldermask();
1971 job.m_3dparams.m_FuseShapes = aCtx.Request.fuse_shapes();
1972 job.m_3dparams.m_FillAllVias = aCtx.Request.fill_all_vias();
1973 job.m_3dparams.m_OptimizeStep = aCtx.Request.optimize_step();
1974 job.m_3dparams.m_ExtraPadThickness = aCtx.Request.extra_pad_thickness();
1975
1977
1978 job.m_vrmlModelDir = wxString::FromUTF8( aCtx.Request.vrml_model_dir() );
1979 job.m_vrmlRelativePaths = aCtx.Request.vrml_relative_paths();
1980
1981 return ExecuteBoardJob( pcbContext(), job );
1982}
1983
1984
1987{
1988 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1989 return tl::unexpected( *busy );
1990
1991 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1992
1993 if( !documentValidation )
1994 return tl::unexpected( documentValidation.error() );
1995
1996 JOB_PCB_RENDER job;
1998 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1999
2002 job.m_bgStyle = FromProtoEnum<JOB_PCB_RENDER::BG_STYLE>( aCtx.Request.background_style() );
2003
2004 job.m_width = aCtx.Request.width();
2005 job.m_height = aCtx.Request.height();
2006 job.m_appearancePreset = aCtx.Request.appearance_preset();
2007 job.m_useBoardStackupColors = aCtx.Request.use_board_stackup_colors();
2008
2010
2011 job.m_zoom = aCtx.Request.zoom();
2012 job.m_perspective = aCtx.Request.perspective();
2013
2014 job.m_rotation = UnpackVector3D( aCtx.Request.rotation() );
2015 job.m_pan = UnpackVector3D( aCtx.Request.pan() );
2016 job.m_pivot = UnpackVector3D( aCtx.Request.pivot() );
2017
2018 job.m_proceduralTextures = aCtx.Request.procedural_textures();
2019 job.m_floor = aCtx.Request.floor();
2020 job.m_antiAlias = aCtx.Request.anti_alias();
2021 job.m_postProcess = aCtx.Request.post_process();
2022
2023 job.m_lightTopIntensity = UnpackVector3D( aCtx.Request.light_top_intensity() );
2024 job.m_lightBottomIntensity = UnpackVector3D( aCtx.Request.light_bottom_intensity() );
2025 job.m_lightCameraIntensity = UnpackVector3D( aCtx.Request.light_camera_intensity() );
2026 job.m_lightSideIntensity = UnpackVector3D( aCtx.Request.light_side_intensity() );
2027 job.m_lightSideElevation = aCtx.Request.light_side_elevation();
2028
2029 return ExecuteBoardJob( pcbContext(), job );
2030}
2031
2032
2035{
2036 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2037 return tl::unexpected( *busy );
2038
2039 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2040
2041 if( !documentValidation )
2042 return tl::unexpected( documentValidation.error() );
2043
2046 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2047
2048 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2049 return tl::unexpected( *err );
2050
2051 job.m_fitPageToBoard = aCtx.Request.fit_page_to_board();
2052 job.m_precision = aCtx.Request.precision();
2053
2054 if( std::optional<ApiResponseStatus> paginationError =
2056 "RunBoardJobExportSvg" ) )
2057 {
2058 return tl::unexpected( *paginationError );
2059 }
2060
2062
2063 return ExecuteBoardJob( pcbContext(), job );
2064}
2065
2066
2069{
2070 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2071 return tl::unexpected( *busy );
2072
2073 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2074
2075 if( !documentValidation )
2076 return tl::unexpected( documentValidation.error() );
2077
2080 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2081
2082 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2083 return tl::unexpected( *err );
2084
2085 job.m_plotGraphicItemsUsingContours = aCtx.Request.plot_graphic_items_using_contours();
2086 job.m_polygonMode = aCtx.Request.polygon_mode();
2087
2088 if( std::optional<ApiResponseStatus> unitError =
2089 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDxf" ) )
2090 {
2091 return tl::unexpected( *unitError );
2092 }
2093
2095
2096 if( std::optional<ApiResponseStatus> paginationError =
2098 "RunBoardJobExportDxf" ) )
2099 {
2100 return tl::unexpected( *paginationError );
2101 }
2102
2104
2105 return ExecuteBoardJob( pcbContext(), job );
2106}
2107
2108
2111{
2112 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2113 return tl::unexpected( *busy );
2114
2115 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2116
2117 if( !documentValidation )
2118 return tl::unexpected( documentValidation.error() );
2119
2122 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2123
2124 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2125 return tl::unexpected( *err );
2126
2127 job.m_pdfFrontFPPropertyPopups = aCtx.Request.front_footprint_property_popups();
2128 job.m_pdfBackFPPropertyPopups = aCtx.Request.back_footprint_property_popups();
2129 job.m_pdfMetadata = aCtx.Request.include_metadata();
2130 job.m_pdfSingle = aCtx.Request.single_document();
2131 job.m_pdfBackgroundColor = wxString::FromUTF8( aCtx.Request.background_color() );
2132
2134
2135 return ExecuteBoardJob( pcbContext(), job );
2136}
2137
2138
2141{
2142 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2143 return tl::unexpected( *busy );
2144
2145 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2146
2147 if( !documentValidation )
2148 return tl::unexpected( documentValidation.error() );
2149
2152 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2153
2154 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2155 return tl::unexpected( *err );
2156
2157 if( std::optional<ApiResponseStatus> paginationError =
2159 "RunBoardJobExportPs" ) )
2160 {
2161 return tl::unexpected( *paginationError );
2162 }
2163
2165
2166 job.m_trackWidthCorrection = aCtx.Request.track_width_correction();
2167 job.m_XScaleAdjust = aCtx.Request.x_scale_adjust();
2168 job.m_YScaleAdjust = aCtx.Request.y_scale_adjust();
2169 job.m_forceA4 = aCtx.Request.force_a4();
2170 job.m_useGlobalSettings = aCtx.Request.use_global_settings();
2171
2172 return ExecuteBoardJob( pcbContext(), job );
2173}
2174
2175
2178{
2179 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2180 return tl::unexpected( *busy );
2181
2182 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2183
2184 if( !documentValidation )
2185 return tl::unexpected( documentValidation.error() );
2186
2187 if( aCtx.Request.layers().empty() )
2188 {
2189 ApiResponseStatus e;
2190 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2191 e.set_error_message( "RunBoardJobExportGerbers requires at least one layer" );
2192 return tl::unexpected( e );
2193 }
2194
2197 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2198
2199 for( int layer : aCtx.Request.layers() )
2200 {
2201 PCB_LAYER_ID layerId =
2203 static_cast<board::types::BoardLayer>( layer ) );
2204
2205 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2206 {
2207 ApiResponseStatus e;
2208 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2209 e.set_error_message( "RunBoardJobExportGerbers contains an invalid layer" );
2210 return tl::unexpected( e );
2211 }
2212
2213 job.m_plotLayerSequence.push_back( layerId );
2214 }
2215
2216 return ExecuteBoardJob( pcbContext(), job );
2217}
2218
2219
2222{
2223 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2224 return tl::unexpected( *busy );
2225
2226 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2227
2228 if( !documentValidation )
2229 return tl::unexpected( documentValidation.error() );
2230
2233 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2234
2236
2237 if( std::optional<ApiResponseStatus> unitError =
2238 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDrill" ) )
2239 {
2240 return tl::unexpected( *unitError );
2241 }
2242
2246
2247 if( aCtx.Request.has_excellon() )
2248 {
2249 const ExcellonFormatOptions& excellonOptions = aCtx.Request.excellon();
2250
2251 if( excellonOptions.has_mirror_y() )
2252 job.m_excellonMirrorY = excellonOptions.mirror_y();
2253
2254 if( excellonOptions.has_minimal_header() )
2255 job.m_excellonMinimalHeader = excellonOptions.minimal_header();
2256
2257 if( excellonOptions.has_combine_pth_npth() )
2258 job.m_excellonCombinePTHNPTH = excellonOptions.combine_pth_npth();
2259
2260 if( excellonOptions.has_route_oval_holes() )
2261 job.m_excellonOvalDrillRoute = excellonOptions.route_oval_holes();
2262 }
2263
2264 if( aCtx.Request.map_format() != DrillMapFormat::DMF_UNKNOWN )
2265 {
2266 job.m_generateMap = true;
2268 }
2269
2270 job.m_gerberPrecision = aCtx.Request.gerber_precision() == DrillGerberPrecision::DGP_4_5 ? 5 : 6;
2271
2272 if( aCtx.Request.has_gerber_generate_tenting() )
2273 job.m_generateTenting = aCtx.Request.gerber_generate_tenting();
2274
2275 if( aCtx.Request.report_format() != DrillReportFormat::DRF_UNKNOWN )
2276 {
2277 job.m_generateReport = true;
2278
2279 if( aCtx.Request.has_report_filename() )
2280 job.m_reportPath = wxString::FromUTF8( aCtx.Request.report_filename() );
2281 }
2282
2283 return ExecuteBoardJob( pcbContext(), job );
2284}
2285
2286
2289{
2290 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2291 return tl::unexpected( *busy );
2292
2293 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2294
2295 if( !documentValidation )
2296 return tl::unexpected( documentValidation.error() );
2297
2300 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2301
2302 if( aCtx.Request.has_use_drill_place_file_origin() )
2303 job.m_useDrillPlaceFileOrigin = aCtx.Request.use_drill_place_file_origin();
2304
2305 job.m_smdOnly = aCtx.Request.smd_only();
2306 job.m_excludeFootprintsWithTh = aCtx.Request.exclude_footprints_with_th();
2307 job.m_excludeDNP = aCtx.Request.exclude_dnp();
2308 job.m_excludeBOM = aCtx.Request.exclude_from_bom();
2309 job.m_negateBottomX = aCtx.Request.negate_bottom_x();
2310 job.m_singleFile = aCtx.Request.single_file();
2311 job.m_nakedFilename = aCtx.Request.naked_filename();
2312 if( aCtx.Request.has_include_board_edge_for_gerber() )
2313 job.m_gerberBoardEdge = aCtx.Request.include_board_edge_for_gerber();
2314
2315 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2316
2318
2319 if( std::optional<ApiResponseStatus> unitError =
2320 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportPosition" ) )
2321 {
2322 return tl::unexpected( *unitError );
2323 }
2324
2327
2328 return ExecuteBoardJob( pcbContext(), job );
2329}
2330
2331
2334{
2335 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2336 return tl::unexpected( *busy );
2337
2338 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2339
2340 if( !documentValidation )
2341 return tl::unexpected( documentValidation.error() );
2342
2345 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2346
2347 job.m_flipBottomPads = aCtx.Request.flip_bottom_pads();
2348 job.m_useIndividualShapes = aCtx.Request.use_individual_shapes();
2349 job.m_storeOriginCoords = aCtx.Request.store_origin_coords();
2350 job.m_useDrillOrigin = aCtx.Request.use_drill_origin();
2351 job.m_useUniquePins = aCtx.Request.use_unique_pins();
2352
2353 return ExecuteBoardJob( pcbContext(), job );
2354}
2355
2356
2359{
2360 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2361 return tl::unexpected( *busy );
2362
2363 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2364
2365 if( !documentValidation )
2366 return tl::unexpected( documentValidation.error() );
2367
2370 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2371
2372 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
2373 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2374 if( aCtx.Request.has_precision() )
2375 job.m_precision = aCtx.Request.precision();
2376
2377 job.m_compress = aCtx.Request.compress();
2378 job.m_colInternalId = wxString::FromUTF8( aCtx.Request.internal_id_column() );
2379 job.m_colMfgPn = wxString::FromUTF8( aCtx.Request.manufacturer_part_number_column() );
2380 job.m_colMfg = wxString::FromUTF8( aCtx.Request.manufacturer_column() );
2381 job.m_colDistPn = wxString::FromUTF8( aCtx.Request.distributor_part_number_column() );
2382 job.m_colDist = wxString::FromUTF8( aCtx.Request.distributor_column() );
2383 job.m_bomRev = wxString::FromUTF8( aCtx.Request.bom_revision() );
2384
2385 if( std::optional<ApiResponseStatus> unitError =
2386 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportIpc2581" ) )
2387 {
2388 return tl::unexpected( *unitError );
2389 }
2390
2393
2394 return ExecuteBoardJob( pcbContext(), job );
2395}
2396
2397
2400{
2401 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2402 return tl::unexpected( *busy );
2403
2404 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2405
2406 if( !documentValidation )
2407 return tl::unexpected( documentValidation.error() );
2408
2411 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2412
2413 return ExecuteBoardJob( pcbContext(), job );
2414}
2415
2416
2419{
2420 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2421 return tl::unexpected( *busy );
2422
2423 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2424
2425 if( !documentValidation )
2426 return tl::unexpected( documentValidation.error() );
2427
2430 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2431
2432 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
2433 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2434 if( aCtx.Request.has_precision() )
2435 job.m_precision = aCtx.Request.precision();
2436
2437 if( std::optional<ApiResponseStatus> unitError =
2438 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportODB" ) )
2439 {
2440 return tl::unexpected( *unitError );
2441 }
2442
2445
2446 return ExecuteBoardJob( pcbContext(), job );
2447}
2448
2449
2452{
2453 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2454 return tl::unexpected( *busy );
2455
2456 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2457
2458 if( !documentValidation )
2459 return tl::unexpected( documentValidation.error() );
2460
2463 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2464
2466
2467 if( std::optional<ApiResponseStatus> unitError =
2468 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportStats" ) )
2469 {
2470 return tl::unexpected( *unitError );
2471 }
2472
2474
2475 job.m_excludeFootprintsWithoutPads = aCtx.Request.exclude_footprints_without_pads();
2476 job.m_subtractHolesFromBoardArea = aCtx.Request.subtract_holes_from_board_area();
2477 job.m_subtractHolesFromCopperAreas = aCtx.Request.subtract_holes_from_copper_areas();
2478
2479 return ExecuteBoardJob( pcbContext(), job );
2480}
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:47
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition api_handler.h:45
std::optional< ApiResponseStatus > ValidatePaginationModeForSingleOrPerFile(kiapi::board::jobs::BoardJobPaginationMode aMode, const std::string &aCommandName)
std::optional< ApiResponseStatus > ApplyBoardPlotSettings(const BoardPlotSettings &aSettings, JOB_EXPORT_PCB_PLOT &aJob)
std::optional< ApiResponseStatus > ValidateUnitsInchMm(types::Units aUnits, const std::string &aCommandName)
HANDLER_RESULT< types::RunJobResponse > ExecuteBoardJob(PCB_CONTEXT *aContext, JOB &aJob)
BASE_SCREEN class implementation.
#define SKIP_CONNECTIVITY
#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
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)
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)
std::optional< TITLE_BLOCK * > getTitleBlock() override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPdf(const HANDLER_CONTEXT< RunBoardJobExportPdf > &aCtx)
HANDLER_RESULT< BoardDesignRulesResponse > handleSetBoardDesignRules(const HANDLER_CONTEXT< SetBoardDesignRules > &aCtx)
API_HANDLER_PCB(PCB_EDIT_FRAME *aFrame)
HANDLER_RESULT< commands::GetItemsResponse > handleGetConnectedItems(const HANDLER_CONTEXT< GetConnectedItems > &aCtx)
HANDLER_RESULT< types::Vector2 > handleGetBoardOrigin(const HANDLER_CONTEXT< GetBoardOrigin > &aCtx)
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsByNetClass(const HANDLER_CONTEXT< GetItemsByNetClass > &aCtx)
bool setPageSettings(const PAGE_INFO &aPageInfo) override
HANDLER_RESULT< NetClassForNetsResponse > handleGetNetClassForNets(const HANDLER_CONTEXT< GetNetClassForNets > &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< 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)
void setDrawingSheetFileName(const wxString &aFileName) override
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsByNet(const HANDLER_CONTEXT< GetItemsByNet > &aCtx)
std::optional< PAGE_INFO > getPageSettings() override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportIpc2581(const HANDLER_CONTEXT< RunBoardJobExportIpc2581 > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPosition(const HANDLER_CONTEXT< RunBoardJobExportPosition > &aCtx)
HANDLER_RESULT< Empty > handleSetBoardOrigin(const HANDLER_CONTEXT< SetBoardOrigin > &aCtx)
HANDLER_RESULT< BoardLayerNameResponse > handleGetBoardLayerName(const HANDLER_CONTEXT< GetBoardLayerName > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportStats(const HANDLER_CONTEXT< RunBoardJobExportStats > &aCtx)
void onModified() override
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportPs(const HANDLER_CONTEXT< RunBoardJobExportPs > &aCtx)
HANDLER_RESULT< CustomRulesResponse > handleGetCustomDesignRules(const HANDLER_CONTEXT< GetCustomDesignRules > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportSvg(const HANDLER_CONTEXT< RunBoardJobExportSvg > &aCtx)
HANDLER_RESULT< commands::GetOpenDocumentsResponse > handleGetOpenDocuments(const HANDLER_CONTEXT< commands::GetOpenDocuments > &aCtx)
HANDLER_RESULT< BoardEditorAppearanceSettings > handleGetBoardEditorAppearanceSettings(const HANDLER_CONTEXT< GetBoardEditorAppearanceSettings > &aCtx)
HANDLER_RESULT< NetsResponse > handleGetNets(const HANDLER_CONTEXT< GetNets > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportDxf(const HANDLER_CONTEXT< RunBoardJobExportDxf > &aCtx)
HANDLER_RESULT< 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< InjectDrcErrorResponse > handleInjectDrcError(const HANDLER_CONTEXT< InjectDrcError > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportRender(const HANDLER_CONTEXT< RunBoardJobExportRender > &aCtx)
PCB_EDIT_FRAME * frame() const
HANDLER_RESULT< types::RunJobResponse > handleRunBoardJobExportGencad(const HANDLER_CONTEXT< RunBoardJobExportGencad > &aCtx)
HANDLER_RESULT< BoardEnabledLayersResponse > handleSetBoardEnabledLayers(const HANDLER_CONTEXT< SetBoardEnabledLayers > &aCtx)
HANDLER_RESULT< Empty > handleSetBoardEditorAppearanceSettings(const HANDLER_CONTEXT< SetBoardEditorAppearanceSettings > &aCtx)
tl::expected< bool, ApiResponseStatus > validateDocumentInternal(const DocumentSpecifier &aDocument) const override
HANDLER_RESULT< Empty > handleRefillZones(const HANDLER_CONTEXT< RefillZones > &aCtx)
HANDLER_RESULT< 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
static wxString m_DrawingSheetFileName
the name of the drawing sheet file, or empty to use the default drawing sheet
Definition base_screen.h:81
void SetContentModified(bool aModified=true)
Definition base_screen.h:55
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< wxString, wxString > m_DrcExclusionComments
std::map< int, SEVERITY > m_DRCSeverities
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
std::set< wxString > m_DrcExclusions
const VECTOR2I & GetGridOrigin() const
TEARDROP_PARAMETERS_LIST m_TeardropParamsList
The parameters of teardrops for the different teardrop targets (via/pad, track end).
const VECTOR2I & GetAuxOrigin() const
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
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
const PAGE_INFO & GetPageSettings() const
Definition board.h:897
void SetDesignSettings(const BOARD_DESIGN_SETTINGS &aSettings)
Definition board.cpp:1155
TITLE_BLOCK & GetTitleBlock()
Definition board.h:903
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition board.h:898
const wxString & GetFileName() const
Definition board.h:410
wxString GetDesignRulesPath() const
Return the absolute path to the design rules file for this board.
Definition board.cpp:272
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1149
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:642
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
void ToProto(kiapi::board::CustomRuleConstraint &aProto) const
Definition drc_rule.cpp:374
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:417
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:80
void ReleaseFile()
Release the current file marked in use.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
JOB_EXPORT_PCB_3D::FORMAT m_format
EXPORTER_STEP_PARAMS m_3dparams
Despite the name; also used for other formats.
ODB_COMPRESSION m_compressionMode
bool m_pdfSingle
This is a hack to deal with cli having the wrong behavior We will deprecate out the wrong behavior,...
GEN_MODE m_pdfGenMode
The background color specified in a hex string.
LSEQ m_plotOnAllLayersSequence
Used by SVG & PDF.
DRILL_MARKS m_drillShapeOption
Used by SVG/DXF/PDF/Gerbers.
bool m_mirror
Common Options.
LSEQ m_plotLayerSequence
Layers to include on all individual layer prints.
wxString m_variant
Variant name for variant-aware filtering.
VECTOR3D m_lightBottomIntensity
VECTOR3D m_lightTopIntensity
VECTOR3D m_lightCameraIntensity
VECTOR3D m_rotation
wxString m_filename
bool m_useBoardStackupColors
VECTOR3D m_lightSideIntensity
std::string m_appearancePreset
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:184
void SetConfiguredOutputPath(const wxString &aPath)
Sets the configured output path for the job, this path is always saved to file.
Definition job.cpp:157
const std::vector< JOB_OUTPUT > & GetOutputs()
Definition job.h:215
const std::string & GetType() const
Definition job.h:195
void SetMirror(bool aMirrorX, bool aMirrorY)
Control the mirroring of the VIEW.
Definition view.cpp:624
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:844
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition view.h:255
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:1552
bool IsMirroredY() const
Return true if view is flipped across the Y axis.
Definition view.h:263
Definition kiid.h:44
std::string AsStdString() const
Definition kiid.cpp:248
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:746
@ FACE_PCB
pcbnew DSO
Definition kiway.h:319
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:38
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:310
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition netclass.cpp:162
Handle the data for a net.
Definition netinfo.h:46
NETCLASS * GetNetClass()
Definition netinfo.h:91
int GetNetCode() const
Definition netinfo.h:94
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:221
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 drillSetOrigin
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
PCBNEW_SETTINGS * GetPcbNewSettings() const
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void SetDisplayOptions(const PCB_DISPLAY_OPTIONS &aOptions, bool aRefresh=true)
Update the current display options.
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 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.
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.
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
Load a KiCad board (.kicad_pcb) from aFileName.
void UpdateUserInterface()
Update the layer manager and other widgets from the board setup (layer and items visibility,...
const KIID GetUUID() const override
Definition pcb_marker.h:45
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition project.cpp:407
std::vector< KIID > KIIDS
Definition rc_item.h:82
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.
A wrapper for reporting to a wxString object.
Definition reporter.h:189
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:813
A type-safe container of any type.
Definition ki_any.h:92
The common library.
PCB_DRC_CODE
Definition drc_item.h:34
@ DRCE_GENERIC_ERROR
Definition drc_item.h:88
@ DRCE_GENERIC_WARNING
Definition drc_item.h:87
#define _(s)
static const std::string KiCadPcbFileExtension
#define KICTL_REVERT
reverting to a previously-saved (KiCad) file.
#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
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)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
KICOMMON_API VECTOR3D UnpackVector3D(const types::Vector3D &aInput)
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)
STL namespace.
std::shared_ptr< PCB_CONTEXT > CreatePcbFrameContext(PCB_EDIT_FRAME *aFrame)
Class to handle a set of BOARD_ITEMs.
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
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
nlohmann::json output
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:71
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:82
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:93
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
#define ZONE_FILLER_TOOL_NAME