KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_handler_pcb.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2023 Jon Evans <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <magic_enum.hpp>
22#include <properties/property.h>
23
24#include <common.h>
25#include <api/api_handler_pcb.h>
26#include <api/api_pcb_utils.h>
27#include <api/api_enums.h>
28#include <api/api_utils.h>
29#include <base_screen.h>
30#include <board_commit.h>
33#include <footprint.h>
34#include <kicad_clipboard.h>
35#include <netinfo.h>
36#include <pad.h>
37#include <pcb_edit_frame.h>
38#include <pcb_group.h>
39#include <pcb_reference_image.h>
40#include <pcb_shape.h>
41#include <pcb_text.h>
42#include <pcb_textbox.h>
43#include <pcb_track.h>
44#include <pcbnew_id.h>
45#include <pcb_marker.h>
46#include <kiway.h>
47#include <drc/drc_item.h>
62#include <jobs/job_pcb_render.h>
63#include <layer_ids.h>
64#include <project.h>
65#include <tool/tool_manager.h>
66#include <tools/pcb_actions.h>
68#include <zone.h>
69
70#include <api/common/types/base_types.pb.h>
73#include <drc/drc_rule_parser.h>
77#include <wx/ffile.h>
78#include <wx/filename.h>
79
80using namespace kiapi::common::commands;
81using types::CommandStatus;
82using types::DocumentType;
83using types::ItemRequestStatus;
84
85
90
91
92API_HANDLER_PCB::API_HANDLER_PCB( std::shared_ptr<PCB_CONTEXT> aContext, PCB_EDIT_FRAME* aFrame ) :
93 API_HANDLER_BOARD( std::move( aContext ), aFrame )
94{
100
102
111
119
126
155
158}
159
160
162{
163 return static_cast<PCB_EDIT_FRAME*>( m_frame );
164}
165
166
169{
170 if( aCtx.Request.type() != DocumentType::DOCTYPE_PCB )
171 {
172 ApiResponseStatus e;
173 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
174 e.set_status( ApiStatusCode::AS_UNHANDLED );
175 return tl::unexpected( e );
176 }
177
178 GetOpenDocumentsResponse response;
179 common::types::DocumentSpecifier doc;
180
181 wxFileName fn( pcbContext()->GetCurrentFileName() );
182
183 doc.set_type( DocumentType::DOCTYPE_PCB );
184 doc.set_board_filename( fn.GetFullName() );
185
186 doc.mutable_project()->set_name( project().GetProjectName().ToStdString() );
187 doc.mutable_project()->set_path( project().GetProjectDirectory().ToStdString() );
188
189 response.mutable_documents()->Add( std::move( doc ) );
190 return response;
191}
192
193
196{
197 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
198 return tl::unexpected( *busy );
199
200 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
201
202 if( !documentValidation )
203 return tl::unexpected( documentValidation.error() );
204
206 return Empty();
207}
208
209
212{
213 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
214 return tl::unexpected( *busy );
215
216 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
217
218 if( !documentValidation )
219 return tl::unexpected( documentValidation.error() );
220
221 wxFileName boardPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.path() ) ) );
222
223 if( !boardPath.IsOk() || !boardPath.IsDirWritable() )
224 {
225 ApiResponseStatus e;
226 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
227 e.set_error_message( fmt::format( "save path '{}' could not be opened",
228 boardPath.GetFullPath().ToStdString() ) );
229 return tl::unexpected( e );
230 }
231
232 if( boardPath.FileExists()
233 && ( !boardPath.IsFileWritable() || !aCtx.Request.options().overwrite() ) )
234 {
235 ApiResponseStatus e;
236 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
237 e.set_error_message( fmt::format( "save path '{}' exists and cannot be overwritten",
238 boardPath.GetFullPath().ToStdString() ) );
239 return tl::unexpected( e );
240 }
241
242 if( boardPath.GetExt() != FILEEXT::KiCadPcbFileExtension )
243 {
244 ApiResponseStatus e;
245 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
246 e.set_error_message( fmt::format( "save path '{}' must have a kicad_pcb extension",
247 boardPath.GetFullPath().ToStdString() ) );
248 return tl::unexpected( e );
249 }
250
251 BOARD* board = this->board();
252
253 if( board->GetFileName().Matches( boardPath.GetFullPath() ) )
254 {
256 return Empty();
257 }
258
259 bool includeProject = true;
260
261 if( aCtx.Request.has_options() )
262 includeProject = aCtx.Request.options().include_project();
263
264 pcbContext()->SavePcbCopy( boardPath.GetFullPath(), includeProject, /* aHeadless = */ true );
265
266 return Empty();
267}
268
269
272{
273 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "RevertDocument" ) )
274 return tl::unexpected( *headless );
275
276 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
277 return tl::unexpected( *busy );
278
279 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
280
281 if( !documentValidation )
282 return tl::unexpected( documentValidation.error() );
283
284 wxFileName fn = project().AbsolutePath( board()->GetFileName() );
285
286 frame()->GetScreen()->SetContentModified( false );
287 frame()->ReleaseFile();
288 frame()->OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ), KICTL_REVERT );
289
290 return Empty();
291}
292
293
294tl::expected<bool, ApiResponseStatus> API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
295{
296 if( aDocument.type() != DocumentType::DOCTYPE_PCB )
297 {
298 ApiResponseStatus e;
299 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
300 e.set_error_message( "the requested document is not a board" );
301 return tl::unexpected( e );
302 }
303
304 wxFileName fn( pcbContext()->GetCurrentFileName() );
305
306 if( aDocument.board_filename().compare( fn.GetFullName() ) != 0 )
307 {
308 ApiResponseStatus e;
309 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
310 e.set_error_message( fmt::format( "the requested document {} is not open",
311 aDocument.board_filename() ) );
312 return tl::unexpected( e );
313 }
314
315 return true;
316}
317
318
320{
321 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
322 return tl::unexpected( *busy );
323
324 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
325 {
326 ApiResponseStatus e;
327 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
328 e.set_status( ApiStatusCode::AS_UNHANDLED );
329 return tl::unexpected( e );
330 }
331
332 GetItemsResponse response;
333
334 BOARD* board = this->board();
335 std::vector<BOARD_ITEM*> items;
336 std::set<KICAD_T> typesRequested, typesInserted;
337 bool handledAnything = false;
338
339 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
340 {
341 typesRequested.emplace( type );
342
343 if( typesInserted.count( type ) )
344 continue;
345
346 switch( type )
347 {
348 case PCB_TRACE_T:
349 case PCB_ARC_T:
350 case PCB_VIA_T:
351 handledAnything = true;
352 std::copy( board->Tracks().begin(), board->Tracks().end(),
353 std::back_inserter( items ) );
354 typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
355 break;
356
357 case PCB_PAD_T:
358 {
359 handledAnything = true;
360
361 for( FOOTPRINT* fp : board->Footprints() )
362 {
363 std::copy( fp->Pads().begin(), fp->Pads().end(),
364 std::back_inserter( items ) );
365 }
366
367 typesInserted.insert( PCB_PAD_T );
368 break;
369 }
370
371 case PCB_FOOTPRINT_T:
372 {
373 handledAnything = true;
374
375 std::copy( board->Footprints().begin(), board->Footprints().end(),
376 std::back_inserter( items ) );
377
378 typesInserted.insert( PCB_FOOTPRINT_T );
379 break;
380 }
381
382 case PCB_SHAPE_T:
383 case PCB_TEXT_T:
384 case PCB_TEXTBOX_T:
385 case PCB_BARCODE_T:
387 {
388 handledAnything = true;
389 bool inserted = false;
390
391 for( BOARD_ITEM* item : board->Drawings() )
392 {
393 if( item->Type() == type )
394 {
395 items.emplace_back( item );
396 inserted = true;
397 }
398 }
399
400 if( inserted )
401 typesInserted.insert( type );
402
403 break;
404 }
405
406 case PCB_DIMENSION_T:
407 {
408 handledAnything = true;
409 bool inserted = false;
410
411 for( BOARD_ITEM* item : board->Drawings() )
412 {
413 switch (item->Type()) {
415 case PCB_DIM_CENTER_T:
416 case PCB_DIM_RADIAL_T:
418 case PCB_DIM_LEADER_T:
419 items.emplace_back( item );
420 inserted = true;
421 break;
422 default:
423 break;
424 }
425 }
426 // we have to add the dimension subtypes to the requested to get them out
428
429 if( inserted )
431
432 break;
433 }
434
435 case PCB_ZONE_T:
436 {
437 handledAnything = true;
438
439 std::copy( board->Zones().begin(), board->Zones().end(),
440 std::back_inserter( items ) );
441
442 typesInserted.insert( PCB_ZONE_T );
443 break;
444 }
445
446 case PCB_GROUP_T:
447 {
448 handledAnything = true;
449
450 std::copy( board->Groups().begin(), board->Groups().end(),
451 std::back_inserter( items ) );
452
453 typesInserted.insert( PCB_GROUP_T );
454 break;
455 }
456 default:
457 break;
458 }
459 }
460
461 if( !handledAnything )
462 {
463 ApiResponseStatus e;
464 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
465 e.set_error_message( "none of the requested types are valid for a Board object" );
466 return tl::unexpected( e );
467 }
468
469 for( const BOARD_ITEM* item : items )
470 {
471 if( !typesRequested.count( item->Type() ) )
472 continue;
473
474 google::protobuf::Any itemBuf;
475 item->Serialize( itemBuf );
476 response.mutable_items()->Add( std::move( itemBuf ) );
477 }
478
479 response.set_status( ItemRequestStatus::IRS_OK );
480 return response;
481}
482
483
486{
487 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
488
489 if( !documentValidation )
490 return tl::unexpected( documentValidation.error() );
491
492 if( aCtx.Request.copper_layer_count() % 2 != 0 )
493 {
494 ApiResponseStatus e;
495 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
496 e.set_error_message( "copper_layer_count must be an even number" );
497 return tl::unexpected( e );
498 }
499
500 if( aCtx.Request.copper_layer_count() > MAX_CU_LAYERS )
501 {
502 ApiResponseStatus e;
503 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
504 e.set_error_message( fmt::format( "copper_layer_count must be below %d", MAX_CU_LAYERS ) );
505 return tl::unexpected( e );
506 }
507
508 int copperLayerCount = static_cast<int>( aCtx.Request.copper_layer_count() );
509 LSET enabled = board::UnpackLayerSet( aCtx.Request.layers() );
510
511 // Sanitize the input
512 enabled |= LSET( { Edge_Cuts, Margin, F_CrtYd, B_CrtYd } );
513 enabled &= ~LSET::AllCuMask();
514 enabled |= LSET::AllCuMask( copperLayerCount );
515
516 BOARD* board = this->board();
517
518 LSET previousEnabled = board->GetEnabledLayers();
519 LSET changedLayers = enabled ^ previousEnabled;
520
521 board->SetEnabledLayers( enabled );
522 board->SetVisibleLayers( board->GetVisibleLayers() | changedLayers );
523
524 LSEQ removedLayers;
525
526 for( PCB_LAYER_ID layer_id : previousEnabled )
527 {
528 if( !enabled[layer_id] && board->HasItemsOnLayer( layer_id ) )
529 removedLayers.push_back( layer_id );
530 }
531
532 bool modified = false;
533
534 if( !removedLayers.empty() )
535 {
537
538 for( PCB_LAYER_ID layer_id : removedLayers )
539 modified |= board->RemoveAllItemsOnLayer( layer_id );
540 }
541
542 if( enabled != previousEnabled )
544
545 if( modified )
546 frame()->OnModify();
547
548 BoardEnabledLayersResponse response;
549
550 response.set_copper_layer_count( copperLayerCount );
551 board::PackLayerSet( *response.mutable_layers(), enabled );
552
553 return response;
554}
555
556
559{
560 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
561
562 if( !documentValidation )
563 return tl::unexpected( documentValidation.error() );
564
566 BoardDesignRulesResponse response;
567 kiapi::board::BoardDesignRules* rules = response.mutable_rules();
568
569 kiapi::board::MinimumConstraints* constraints = rules->mutable_constraints();
570
571 constraints->mutable_min_clearance()->set_value_nm( bds.m_MinClearance );
572 constraints->mutable_min_groove_width()->set_value_nm( bds.m_MinGrooveWidth );
573 constraints->mutable_min_connection_width()->set_value_nm( bds.m_MinConn );
574 constraints->mutable_min_track_width()->set_value_nm( bds.m_TrackMinWidth );
575 constraints->mutable_min_via_annular_width()->set_value_nm( bds.m_ViasMinAnnularWidth );
576 constraints->mutable_min_via_size()->set_value_nm( bds.m_ViasMinSize );
577 constraints->mutable_min_through_drill()->set_value_nm( bds.m_MinThroughDrill );
578 constraints->mutable_min_microvia_size()->set_value_nm( bds.m_MicroViasMinSize );
579 constraints->mutable_min_microvia_drill()->set_value_nm( bds.m_MicroViasMinDrill );
580 constraints->mutable_copper_edge_clearance()->set_value_nm( bds.m_CopperEdgeClearance );
581 constraints->mutable_hole_clearance()->set_value_nm( bds.m_HoleClearance );
582 constraints->mutable_hole_to_hole_min()->set_value_nm( bds.m_HoleToHoleMin );
583 constraints->mutable_silk_clearance()->set_value_nm( bds.m_SilkClearance );
584 constraints->set_min_resolved_spokes( bds.m_MinResolvedSpokes );
585 constraints->mutable_min_silk_text_height()->set_value_nm( bds.m_MinSilkTextHeight );
586 constraints->mutable_min_silk_text_thickness()->set_value_nm( bds.m_MinSilkTextThickness );
587
588 kiapi::board::PredefinedSizes* sizes = rules->mutable_predefined_sizes();
589
590 for( size_t ii = 1; ii < bds.m_TrackWidthList.size(); ++ii )
591 sizes->add_tracks()->mutable_width()->set_value_nm( bds.m_TrackWidthList[ii] );
592
593 for( size_t ii = 1; ii < bds.m_ViasDimensionsList.size(); ++ii )
594 {
595 kiapi::board::PresetViaDimension* via = sizes->add_vias();
596 via->mutable_diameter()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Diameter );
597 via->mutable_drill()->set_value_nm( bds.m_ViasDimensionsList[ii].m_Drill );
598 }
599
600 for( size_t ii = 1; ii < bds.m_DiffPairDimensionsList.size(); ++ii )
601 {
602 kiapi::board::PresetDiffPairDimension* pair = sizes->add_diff_pairs();
603 pair->mutable_width()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Width );
604 pair->mutable_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_Gap );
605 pair->mutable_via_gap()->set_value_nm( bds.m_DiffPairDimensionsList[ii].m_ViaGap );
606 }
607
608 kiapi::board::SolderMaskPasteDefaults* maskPaste = rules->mutable_solder_mask_paste();
609
610 maskPaste->mutable_mask_expansion()->set_value_nm( bds.m_SolderMaskExpansion );
611 maskPaste->mutable_mask_min_width()->set_value_nm( bds.m_SolderMaskMinWidth );
612 maskPaste->mutable_mask_to_copper_clearance()->set_value_nm( bds.m_SolderMaskToCopperClearance );
613 maskPaste->mutable_paste_margin()->set_value_nm( bds.m_SolderPasteMargin );
614 maskPaste->set_paste_margin_ratio( bds.m_SolderPasteMarginRatio );
615 maskPaste->set_allow_soldermask_bridges_in_footprints( bds.m_AllowSoldermaskBridgesInFPs );
616
617 kiapi::board::TeardropDefaults* teardrops = rules->mutable_teardrops();
618
619 teardrops->set_target_vias( bds.m_TeardropParamsList.m_TargetVias );
620 teardrops->set_target_pth_pads( bds.m_TeardropParamsList.m_TargetPTHPads );
621 teardrops->set_target_smd_pads( bds.m_TeardropParamsList.m_TargetSMDPads );
622 teardrops->set_target_track_to_track( bds.m_TeardropParamsList.m_TargetTrack2Track );
623 teardrops->set_use_round_shapes_only( bds.m_TeardropParamsList.m_UseRoundShapesOnly );
624
626
627 for( int target = TARGET_ROUND; target <= TARGET_TRACK; ++target )
628 {
629 const TEARDROP_PARAMETERS* params = tdList.GetParameters( static_cast<TARGET_TD>( target ) );
630 kiapi::board::TeardropTargetEntry* entry = teardrops->add_target_params();
631
633 static_cast<TARGET_TD>( target ) ) );
634 entry->mutable_params()->set_enabled( params->m_Enabled );
635 entry->mutable_params()->mutable_max_length()->set_value_nm( params->m_TdMaxLen );
636 entry->mutable_params()->mutable_max_width()->set_value_nm( params->m_TdMaxWidth );
637 entry->mutable_params()->set_best_length_ratio( params->m_BestLengthRatio );
638 entry->mutable_params()->set_best_width_ratio( params->m_BestWidthRatio );
639 entry->mutable_params()->set_width_to_size_filter_ratio( params->m_WidthtoSizeFilterRatio );
640 entry->mutable_params()->set_curved_edges( params->m_CurvedEdges );
641 entry->mutable_params()->set_allow_two_tracks( params->m_AllowUseTwoTracks );
642 entry->mutable_params()->set_on_pads_in_zones( params->m_TdOnPadsInZones );
643 }
644
645 kiapi::board::ViaProtectionDefaults* viaProtection = rules->mutable_via_protection();
646
647 viaProtection->set_tent_front( bds.m_TentViasFront );
648 viaProtection->set_tent_back( bds.m_TentViasBack );
649 viaProtection->set_cover_front( bds.m_CoverViasFront );
650 viaProtection->set_cover_back( bds.m_CoverViasBack );
651 viaProtection->set_plug_front( bds.m_PlugViasFront );
652 viaProtection->set_plug_back( bds.m_PlugViasBack );
653 viaProtection->set_cap( bds.m_CapVias );
654 viaProtection->set_fill( bds.m_FillVias );
655
656 for( const auto& [errorCode, severity] : bds.m_DRCSeverities )
657 {
658 board::DrcSeveritySetting* setting = rules->add_severities();
659 setting->set_rule_type(
661 setting->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( severity ) );
662 }
663
664 for( const wxString& serialized : bds.m_DrcExclusions )
665 {
666 kiapi::board::DrcExclusion* exclusion = rules->add_exclusions();
667 exclusion->mutable_marker()->mutable_id()->set_opaque_id( serialized.ToStdString() );
668
669 auto it = bds.m_DrcExclusionComments.find( serialized );
670
671 if( it != bds.m_DrcExclusionComments.end() )
672 exclusion->set_comment( it->second.ToStdString() );
673 }
674
675 response.set_custom_rules_status( CRS_NONE );
676
677 wxFileName fn = pcbContext()->GetBoard()->GetFileName();
679 wxString rulesPath = pcbContext()->Prj().AbsolutePath( fn.GetFullName() );
680
681 if( !rulesPath.IsEmpty() && wxFileName::IsFileReadable( rulesPath ) )
682 {
683 wxFFile file( rulesPath, "r" );
684 wxString content;
685 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
686
687 if( !file.IsOpened() )
688 {
689 response.set_custom_rules_status( CRS_INVALID );
690 return response;
691 }
692
693 file.ReadAll( &content );
694 file.Close();
695
696 try
697 {
698 DRC_RULES_PARSER parser( content, "File" );
699 parser.Parse( parsedRules, nullptr );
700 response.set_custom_rules_status( CRS_VALID );
701 }
702 catch( const IO_ERROR& )
703 {
704 response.set_custom_rules_status( CRS_INVALID );
705 }
706 }
707
708 return response;
709}
710
711
714{
715 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
716
717 if( !documentValidation )
718 return tl::unexpected( documentValidation.error() );
719
720 BOARD_DESIGN_SETTINGS newSettings( board()->GetDesignSettings() );
721 const kiapi::board::BoardDesignRules& rules = aCtx.Request.rules();
722
723 if( rules.has_constraints() )
724 {
725 const kiapi::board::MinimumConstraints& constraints = rules.constraints();
726
727 newSettings.m_MinClearance = constraints.min_clearance().value_nm();
728 newSettings.m_MinGrooveWidth = constraints.min_groove_width().value_nm();
729 newSettings.m_MinConn = constraints.min_connection_width().value_nm();
730 newSettings.m_TrackMinWidth = constraints.min_track_width().value_nm();
731 newSettings.m_ViasMinAnnularWidth = constraints.min_via_annular_width().value_nm();
732 newSettings.m_ViasMinSize = constraints.min_via_size().value_nm();
733 newSettings.m_MinThroughDrill = constraints.min_through_drill().value_nm();
734 newSettings.m_MicroViasMinSize = constraints.min_microvia_size().value_nm();
735 newSettings.m_MicroViasMinDrill = constraints.min_microvia_drill().value_nm();
736 newSettings.m_CopperEdgeClearance = constraints.copper_edge_clearance().value_nm();
737 newSettings.m_HoleClearance = constraints.hole_clearance().value_nm();
738 newSettings.m_HoleToHoleMin = constraints.hole_to_hole_min().value_nm();
739 newSettings.m_SilkClearance = constraints.silk_clearance().value_nm();
740 newSettings.m_MinResolvedSpokes = constraints.min_resolved_spokes();
741 newSettings.m_MinSilkTextHeight = constraints.min_silk_text_height().value_nm();
742 newSettings.m_MinSilkTextThickness = constraints.min_silk_text_thickness().value_nm();
743 }
744
745 if( rules.has_predefined_sizes() )
746 {
747 newSettings.m_TrackWidthList.clear();
748 newSettings.m_TrackWidthList.emplace_back( 0 );
749
750 for( const kiapi::board::PresetTrackWidth& track : rules.predefined_sizes().tracks() )
751 newSettings.m_TrackWidthList.emplace_back( track.width().value_nm() );
752
753 newSettings.m_ViasDimensionsList.clear();
754 newSettings.m_ViasDimensionsList.emplace_back( 0, 0 );
755
756 for( const kiapi::board::PresetViaDimension& via : rules.predefined_sizes().vias() )
757 {
758 newSettings.m_ViasDimensionsList.emplace_back( static_cast<int>( via.diameter().value_nm() ),
759 static_cast<int>( via.drill().value_nm() ) );
760 }
761
762 newSettings.m_DiffPairDimensionsList.clear();
763 newSettings.m_DiffPairDimensionsList.emplace_back( 0, 0, 0 );
764
765 for( const kiapi::board::PresetDiffPairDimension& pair : rules.predefined_sizes().diff_pairs() )
766 {
767 newSettings.m_DiffPairDimensionsList.emplace_back(
768 static_cast<int>( pair.width().value_nm() ),
769 static_cast<int>( pair.gap().value_nm() ),
770 static_cast<int>( pair.via_gap().value_nm() ) );
771 }
772 }
773
774 if( rules.has_solder_mask_paste() )
775 {
776 const kiapi::board::SolderMaskPasteDefaults& maskPaste = rules.solder_mask_paste();
777
778 newSettings.m_SolderMaskExpansion = maskPaste.mask_expansion().value_nm();
779 newSettings.m_SolderMaskMinWidth = maskPaste.mask_min_width().value_nm();
780 newSettings.m_SolderMaskToCopperClearance = maskPaste.mask_to_copper_clearance().value_nm();
781 newSettings.m_SolderPasteMargin = maskPaste.paste_margin().value_nm();
782 newSettings.m_SolderPasteMarginRatio = maskPaste.paste_margin_ratio();
784 maskPaste.allow_soldermask_bridges_in_footprints();
785 }
786
787 if( rules.has_teardrops() )
788 {
789 const kiapi::board::TeardropDefaults& teardrops = rules.teardrops();
790
791 newSettings.m_TeardropParamsList.m_TargetVias = teardrops.target_vias();
792 newSettings.m_TeardropParamsList.m_TargetPTHPads = teardrops.target_pth_pads();
793 newSettings.m_TeardropParamsList.m_TargetSMDPads = teardrops.target_smd_pads();
794 newSettings.m_TeardropParamsList.m_TargetTrack2Track = teardrops.target_track_to_track();
795 newSettings.m_TeardropParamsList.m_UseRoundShapesOnly = teardrops.use_round_shapes_only();
796
797 for( const kiapi::board::TeardropTargetEntry& entry : teardrops.target_params() )
798 {
799 if( entry.target() == kiapi::board::TeardropTarget::TDT_UNKNOWN )
800 continue;
801
803 entry.target() );
804
805 TEARDROP_PARAMETERS* params = newSettings.m_TeardropParamsList.GetParameters( target );
806
807 params->m_Enabled = entry.params().enabled();
808 params->m_TdMaxLen = entry.params().max_length().value_nm();
809 params->m_TdMaxWidth = entry.params().max_width().value_nm();
810 params->m_BestLengthRatio = entry.params().best_length_ratio();
811 params->m_BestWidthRatio = entry.params().best_width_ratio();
812 params->m_WidthtoSizeFilterRatio = entry.params().width_to_size_filter_ratio();
813 params->m_CurvedEdges = entry.params().curved_edges();
814 params->m_AllowUseTwoTracks = entry.params().allow_two_tracks();
815 params->m_TdOnPadsInZones = entry.params().on_pads_in_zones();
816 }
817 }
818
819 if( rules.has_via_protection() )
820 {
821 const kiapi::board::ViaProtectionDefaults& viaProtection = rules.via_protection();
822
823 newSettings.m_TentViasFront = viaProtection.tent_front();
824 newSettings.m_TentViasBack = viaProtection.tent_back();
825 newSettings.m_CoverViasFront = viaProtection.cover_front();
826 newSettings.m_CoverViasBack = viaProtection.cover_back();
827 newSettings.m_PlugViasFront = viaProtection.plug_front();
828 newSettings.m_PlugViasBack = viaProtection.plug_back();
829 newSettings.m_CapVias = viaProtection.cap();
830 newSettings.m_FillVias = viaProtection.fill();
831 }
832
833 if( rules.severities_size() > 0 )
834 {
835 newSettings.m_DRCSeverities.clear();
836
837 for( const kiapi::board::DrcSeveritySetting& severitySetting : rules.severities() )
838 {
839 PCB_DRC_CODE ruleType =
840 FromProtoEnum<PCB_DRC_CODE, kiapi::board::DrcErrorType>( severitySetting.rule_type() );
841
842 const std::unordered_set<SEVERITY> permitted( { RPT_SEVERITY_ERROR, RPT_SEVERITY_WARNING, RPT_SEVERITY_IGNORE } );
843 SEVERITY setting = FromProtoEnum<SEVERITY, kiapi::common::types::RuleSeverity>( severitySetting.severity() );
844
845 if( !permitted.contains( setting ) )
846 {
847 ApiResponseStatus e;
848 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
849 e.set_error_message( fmt::format( "DRC severity must be error, warning, or ignore" ) );
850 return tl::unexpected( e );
851 }
852
853 newSettings.m_DRCSeverities[ruleType] = setting;
854 }
855 }
856
857 if( rules.exclusions_size() > 0 )
858 {
859 newSettings.m_DrcExclusions.clear();
860 newSettings.m_DrcExclusionComments.clear();
861
862 for( const kiapi::board::DrcExclusion& exclusion : rules.exclusions() )
863 {
864 wxString serialized = wxString::FromUTF8( exclusion.marker().id().opaque_id() );
865
866 if( serialized.IsEmpty() )
867 {
868 ApiResponseStatus e;
869 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
870 e.set_error_message( "DrcExclusion marker id must not be empty" );
871 return tl::unexpected( e );
872 }
873
874 newSettings.m_DrcExclusions.insert( serialized );
875 newSettings.m_DrcExclusionComments[serialized] = wxString::FromUTF8( exclusion.comment() );
876 }
877 }
878
879 std::vector<BOARD_DESIGN_SETTINGS::VALIDATION_ERROR> errors = newSettings.ValidateDesignRules();
880
881 if( !errors.empty() )
882 {
883 const BOARD_DESIGN_SETTINGS::VALIDATION_ERROR& error = errors.front();
884
885 ApiResponseStatus e;
886 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
887 e.set_error_message( fmt::format( "Invalid board design rules: {}: {}",
888 error.setting_name.ToStdString(),
889 error.error_message.ToStdString() ) );
890 return tl::unexpected( e );
891 }
892
893 board()->SetDesignSettings( newSettings );
894
895 if( frame() )
896 {
897 frame()->OnModify();
899 }
900
901 HANDLER_CONTEXT<GetBoardDesignRules> getCtx = { aCtx.ClientName, GetBoardDesignRules() };
902 *getCtx.Request.mutable_board() = aCtx.Request.board();
903
904 return handleGetBoardDesignRules( getCtx );
905}
906
907
910{
911 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
912
913 if( !documentValidation )
914 return tl::unexpected( documentValidation.error() );
915
916 CustomRulesResponse response;
917 response.set_status( CRS_NONE );
918
919 wxFileName fn = pcbContext()->GetBoard()->GetFileName();
921 wxString rulesPath = pcbContext()->Prj().AbsolutePath( fn.GetFullName() );
922
923 if( rulesPath.IsEmpty() || !wxFileName::IsFileReadable( rulesPath ) )
924 return response;
925
926 wxFFile file( rulesPath, "r" );
927
928 if( !file.IsOpened() )
929 {
930 response.set_status( CRS_INVALID );
931 response.set_error_text( "Failed to open custom rules file" );
932 return response;
933 }
934
935 wxString content;
936 file.ReadAll( &content );
937 file.Close();
938
939 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
940
941 try
942 {
943 DRC_RULES_PARSER parser( content, "File" );
944 parser.Parse( parsedRules, nullptr );
945 }
946 catch( const IO_ERROR& ioe )
947 {
948 response.set_status( CRS_INVALID );
949 response.set_error_text( ioe.What().ToStdString() );
950 return response;
951 }
952
953 for( const std::shared_ptr<DRC_RULE>& rule : parsedRules )
954 {
955 // TODO(JE) since we now need this for both here and the rules editor, maybe it's time
956 // to just make comment parsing part of the parser?
957 wxString text = DRC_RULE_LOADER::ExtractRuleText( content, rule->m_Name );
958 wxString comment = DRC_RULE_LOADER::ExtractRuleComment( text );
959
960 kiapi::board::CustomRule* customRule = response.add_rules();
961
962 if( rule->m_Condition )
963 customRule->set_condition( rule->m_Condition->GetExpression().ToUTF8() );
964
965 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
966 {
967 board::CustomRuleConstraint* constraintProto = customRule->add_constraints();
968 constraint.ToProto( *constraintProto );
969 }
970
971 customRule->set_severity( ToProtoEnum<SEVERITY, types::RuleSeverity>( rule->m_Severity ) );
972 customRule->set_name( rule->m_Name.ToUTF8() );
973
974 if( rule->m_LayerSource.CmpNoCase( wxS( "outer" ) ) == 0 )
975 {
976 customRule->set_layer_mode( kiapi::board::CRLM_OUTER );
977 }
978 else if( rule->m_LayerSource.CmpNoCase( wxS( "inner" ) ) == 0 )
979 {
980 customRule->set_layer_mode( kiapi::board::CRLM_INNER );
981 }
982 else if( !rule->m_LayerSource.IsEmpty() )
983 {
984 int layer = LSET::NameToLayer( rule->m_LayerSource );
985
986 if( layer != UNDEFINED_LAYER && layer != UNSELECTED_LAYER && layer < PCB_LAYER_ID_COUNT )
987 {
988 customRule->set_single_layer(
990 }
991 }
992
993 if( !comment.IsEmpty() )
994 customRule->set_comments( comment );
995 }
996
997 response.set_status( CRS_VALID );
998 return response;
999}
1000
1001
1004{
1005 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1006
1007 if( !documentValidation )
1008 return tl::unexpected( documentValidation.error() );
1009
1010 wxFileName fn = pcbContext()->GetBoard()->GetFileName();
1012 wxString rulesPath = pcbContext()->Prj().AbsolutePath( fn.GetFullName() );
1013
1014 if( aCtx.Request.rules_size() == 0 )
1015 {
1016 if( wxFileName::FileExists( rulesPath ) )
1017 {
1018 if( !wxRemoveFile( rulesPath ) )
1019 {
1020 CustomRulesResponse response;
1021 response.set_status( CRS_INVALID );
1022 response.set_error_text( "Failed to remove custom rules file" );
1023 return response;
1024 }
1025 }
1026
1027 CustomRulesResponse response;
1028 response.set_status( CRS_NONE );
1029 return response;
1030 }
1031
1032 wxString rulesText;
1033 rulesText << "(version 1)\n";
1034
1035 for( const board::CustomRule& rule : aCtx.Request.rules() )
1036 {
1037 wxString serializationError;
1038 wxString serializedRule = DRC_RULE::FormatRuleFromProto( rule, &serializationError );
1039
1040 if( serializedRule.IsEmpty() )
1041 {
1042 CustomRulesResponse response;
1043 response.set_status( CRS_INVALID );
1044
1045 if( serializationError.IsEmpty() )
1046 response.set_error_text( "Failed to serialize custom rule" );
1047 else
1048 response.set_error_text( serializationError.ToUTF8() );
1049
1050 return response;
1051 }
1052
1053 rulesText << "\n" << serializedRule;
1054 }
1055
1056 // Validate generated file text before writing so callers get parser errors in response.
1057 try
1058 {
1059 std::vector<std::shared_ptr<DRC_RULE>> parsedRules;
1060 DRC_RULES_PARSER parser( rulesText, "SetCustomDesignRules" );
1061 parser.Parse( parsedRules, nullptr );
1062 }
1063 catch( const IO_ERROR& ioe )
1064 {
1065 CustomRulesResponse response;
1066 response.set_status( CRS_INVALID );
1067 response.set_error_text( ioe.What().ToStdString() );
1068 return response;
1069 }
1070
1071 wxFFile file( rulesPath, "w" );
1072
1073 if( !file.IsOpened() )
1074 {
1075 CustomRulesResponse response;
1076 response.set_status( CRS_INVALID );
1077 response.set_error_text( "Failed to open custom rules file for writing" );
1078 return response;
1079 }
1080
1081 if( !file.Write( rulesText ) )
1082 {
1083 file.Close();
1084
1085 CustomRulesResponse response;
1086 response.set_status( CRS_INVALID );
1087 response.set_error_text( "Failed to write custom rules file" );
1088 return response;
1089 }
1090
1091 file.Close();
1092
1093 HANDLER_CONTEXT<GetCustomDesignRules> getCtx = { aCtx.ClientName, GetCustomDesignRules() };
1094 *getCtx.Request.mutable_board() = aCtx.Request.board();
1095 return handleGetCustomDesignRules( getCtx );
1096}
1097
1098
1101{
1102 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1103 !documentValidation )
1104 {
1105 return tl::unexpected( documentValidation.error() );
1106 }
1107
1108 VECTOR2I origin;
1109 const BOARD_DESIGN_SETTINGS& settings = board()->GetDesignSettings();
1110
1111 switch( aCtx.Request.type() )
1112 {
1113 case BOT_GRID:
1114 origin = settings.GetGridOrigin();
1115 break;
1116
1117 case BOT_DRILL:
1118 origin = settings.GetAuxOrigin();
1119 break;
1120
1121 default:
1122 case BOT_UNKNOWN:
1123 {
1124 ApiResponseStatus e;
1125 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1126 e.set_error_message( "Unexpected origin type" );
1127 return tl::unexpected( e );
1128 }
1129 }
1130
1131 types::Vector2 reply;
1132 PackVector2( reply, origin );
1133 return reply;
1134}
1135
1138{
1139 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1140 return tl::unexpected( *busy );
1141
1142 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1143 !documentValidation )
1144 {
1145 return tl::unexpected( documentValidation.error() );
1146 }
1147
1148 VECTOR2I origin = UnpackVector2( aCtx.Request.origin() );
1149
1150 switch( aCtx.Request.type() )
1151 {
1152 case BOT_GRID:
1153 {
1154 PCB_EDIT_FRAME* f = frame();
1155
1156 frame()->CallAfter( [f, origin]()
1157 {
1158 // gridSetOrigin takes ownership and frees this
1159 VECTOR2D* dorigin = new VECTOR2D( origin );
1160 TOOL_MANAGER* mgr = f->GetToolManager();
1161 mgr->RunAction( PCB_ACTIONS::gridSetOrigin, dorigin );
1162 f->Refresh();
1163 } );
1164 break;
1165 }
1166
1167 case BOT_DRILL:
1168 {
1169 PCB_EDIT_FRAME* f = frame();
1170
1171 frame()->CallAfter( [f, origin]()
1172 {
1173 TOOL_MANAGER* mgr = f->GetToolManager();
1174 mgr->RunAction( PCB_ACTIONS::drillSetOrigin, origin );
1175 f->Refresh();
1176 } );
1177 break;
1178 }
1179
1180 default:
1181 case BOT_UNKNOWN:
1182 {
1183 ApiResponseStatus e;
1184 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1185 e.set_error_message( "Unexpected origin type" );
1186 return tl::unexpected( e );
1187 }
1188 }
1189
1190 return Empty();
1191}
1192
1193
1196{
1197 if( HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1198 !documentValidation )
1199 {
1200 return tl::unexpected( documentValidation.error() );
1201 }
1202
1203 BoardLayerNameResponse response;
1204
1206
1207 response.set_name( board()->GetLayerName( id ) );
1208
1209 return response;
1210}
1211
1212
1213std::optional<TITLE_BLOCK*> API_HANDLER_PCB::getTitleBlock()
1214{
1215 return &context()->GetBoard()->GetTitleBlock();
1216}
1217
1218
1219std::optional<PAGE_INFO> API_HANDLER_PCB::getPageSettings()
1220{
1221 return context()->GetBoard()->GetPageSettings();
1222}
1223
1224
1226{
1227 context()->GetBoard()->SetPageSettings( aPageInfo );
1228 return true;
1229}
1230
1231
1236
1237
1238void API_HANDLER_PCB::setDrawingSheetFileName( const wxString& aFileName )
1239{
1241
1242 if( frame() )
1244}
1245
1246
1248{
1249 if( frame() )
1250 {
1251 frame()->Refresh();
1252 frame()->OnModify();
1254 }
1255}
1256
1257
1259{
1260 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1261
1262 if( !documentValidation )
1263 return tl::unexpected( documentValidation.error() );
1264
1265 NetsResponse response;
1266 BOARD* board = this->board();
1267
1268 std::set<wxString> netclassFilter;
1269
1270 for( const std::string& nc : aCtx.Request.netclass_filter() )
1271 netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) );
1272
1273 for( NETINFO_ITEM* net : board->GetNetInfo() )
1274 {
1275 NETCLASS* nc = net->GetNetClass();
1276
1277 if( !netclassFilter.empty() && nc )
1278 {
1279 bool inClass = false;
1280
1281 for( const wxString& filter : netclassFilter )
1282 {
1283 if( nc->ContainsNetclassWithName( filter ) )
1284 {
1285 inClass = true;
1286 break;
1287 }
1288 }
1289
1290 if( !inClass )
1291 continue;
1292 }
1293
1294 board::types::Net* netProto = response.add_nets();
1295 netProto->set_name( net->GetNetname() );
1296 netProto->mutable_code()->set_value( net->GetNetCode() );
1297 }
1298
1299 return response;
1300}
1301
1302
1305{
1306 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1307 return tl::unexpected( *busy );
1308
1309 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1310 {
1311 ApiResponseStatus e;
1312 e.set_status( ApiStatusCode::AS_UNHANDLED );
1313 return tl::unexpected( e );
1314 }
1315
1316 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1317 const bool filterByType = aCtx.Request.types_size() > 0;
1318
1319 if( filterByType && types.empty() )
1320 {
1321 ApiResponseStatus e;
1322 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1323 e.set_error_message( "none of the requested types are valid for a Board object" );
1324 return tl::unexpected( e );
1325 }
1326
1327 std::set<KICAD_T> typeFilter( types.begin(), types.end() );
1328 std::vector<BOARD_CONNECTED_ITEM*> sourceItems;
1329
1330 for( const types::KIID& id : aCtx.Request.items() )
1331 {
1332 if( std::optional<BOARD_ITEM*> item = getItemById( KIID( id.value() ) ) )
1333 {
1334 if( BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( *item ) )
1335 sourceItems.emplace_back( connected );
1336 }
1337 }
1338
1339 if( sourceItems.empty() )
1340 {
1341 ApiResponseStatus e;
1342 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1343 e.set_error_message( "none of the requested IDs were found or valid connected items" );
1344 return tl::unexpected( e );
1345 }
1346
1347 GetItemsResponse response;
1348 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
1349 std::set<KIID> insertedItems;
1350
1351 for( BOARD_CONNECTED_ITEM* source : sourceItems )
1352 {
1353 for( BOARD_CONNECTED_ITEM* connected : conn->GetConnectedItems( source ) )
1354 {
1355 if( filterByType && !typeFilter.contains( connected->Type() ) )
1356 continue;
1357
1358 if( !insertedItems.insert( connected->m_Uuid ).second )
1359 continue;
1360
1361 connected->Serialize( *response.add_items() );
1362 }
1363 }
1364
1365 response.set_status( ItemRequestStatus::IRS_OK );
1366 return response;
1367}
1368
1369
1371 const HANDLER_CONTEXT<GetItemsByNet>& aCtx )
1372{
1373 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1374 return tl::unexpected( *busy );
1375
1376 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1377 {
1378 ApiResponseStatus e;
1379 e.set_status( ApiStatusCode::AS_UNHANDLED );
1380 return tl::unexpected( e );
1381 }
1382
1383 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1384 const bool filterByType = aCtx.Request.types_size() > 0;
1385
1386 if( filterByType && types.empty() )
1387 {
1388 ApiResponseStatus e;
1389 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1390 e.set_error_message( "none of the requested types are valid for a Board object" );
1391 return tl::unexpected( e );
1392 }
1393
1394 if( !filterByType )
1396
1397 GetItemsResponse response;
1398 BOARD* board = this->board();
1399 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
1400 std::set<KIID> insertedItems;
1401
1402 const NETINFO_LIST& nets = board->GetNetInfo();
1403
1404 for( const board::types::Net& net : aCtx.Request.nets() )
1405 {
1406 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
1407
1408 if( !netInfo )
1409 continue;
1410
1411 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( netInfo->GetNetCode(), types ) )
1412 {
1413 if( !insertedItems.insert( item->m_Uuid ).second )
1414 continue;
1415
1416 item->Serialize( *response.add_items() );
1417 }
1418 }
1419
1420 response.set_status( ItemRequestStatus::IRS_OK );
1421 return response;
1422}
1423
1424
1427{
1428 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1429 return tl::unexpected( *busy );
1430
1431 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
1432 {
1433 ApiResponseStatus e;
1434 e.set_status( ApiStatusCode::AS_UNHANDLED );
1435 return tl::unexpected( e );
1436 }
1437
1438 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1439 const bool filterByType = aCtx.Request.types_size() > 0;
1440
1441 if( filterByType && types.empty() )
1442 {
1443 ApiResponseStatus e;
1444 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1445 e.set_error_message( "none of the requested types are valid for a Board object" );
1446 return tl::unexpected( e );
1447 }
1448
1449 if( !filterByType )
1451
1452 std::set<wxString> requestedClasses;
1453
1454 for( const std::string& netClass : aCtx.Request.net_classes() )
1455 requestedClasses.insert( wxString( netClass.c_str(), wxConvUTF8 ) );
1456
1457 GetItemsResponse response;
1458 BOARD* board = this->board();
1459 std::shared_ptr<CONNECTIVITY_DATA> conn = board->GetConnectivity();
1460 std::set<KIID> insertedItems;
1461
1462 for( NETINFO_ITEM* net : board->GetNetInfo() )
1463 {
1464 if( !net )
1465 continue;
1466
1467 NETCLASS* nc = net->GetNetClass();
1468
1469 if( !requestedClasses.empty() )
1470 {
1471 if( !nc )
1472 continue;
1473
1474 bool inClass = false;
1475
1476 for( const wxString& filter : requestedClasses )
1477 {
1478 if( nc->ContainsNetclassWithName( filter ) )
1479 {
1480 inClass = true;
1481 break;
1482 }
1483 }
1484
1485 if( !inClass )
1486 continue;
1487 }
1488
1489 for( BOARD_CONNECTED_ITEM* item : conn->GetNetItems( net->GetNetCode(), types ) )
1490 {
1491 if( !insertedItems.insert( item->m_Uuid ).second )
1492 continue;
1493
1494 item->Serialize( *response.add_items() );
1495 }
1496 }
1497
1498 response.set_status( ItemRequestStatus::IRS_OK );
1499 return response;
1500}
1501
1502
1505{
1506 NetClassForNetsResponse response;
1507
1508 BOARD* board = this->board();
1509 const NETINFO_LIST& nets = board->GetNetInfo();
1510 google::protobuf::Any any;
1511
1512 for( const board::types::Net& net : aCtx.Request.net() )
1513 {
1514 NETINFO_ITEM* netInfo = nets.GetNetItem( wxString::FromUTF8( net.name() ) );
1515
1516 if( !netInfo )
1517 continue;
1518
1519 netInfo->GetNetClass()->Serialize( any );
1520 auto [pair, rc] = response.mutable_classes()->insert( { net.name(), {} } );
1521 any.UnpackTo( &pair->second );
1522 }
1523
1524 return response;
1525}
1526
1527
1529{
1530 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1531 return tl::unexpected( *busy );
1532
1533 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1534
1535 if( !documentValidation )
1536 return tl::unexpected( documentValidation.error() );
1537
1538 if( aCtx.Request.zones().empty() )
1539 {
1540 TOOL_MANAGER* mgr = toolManager();
1541 frame()->CallAfter( [mgr]()
1542 {
1544 } );
1545 }
1546 else
1547 {
1548 // TODO
1549 ApiResponseStatus e;
1550 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
1551 return tl::unexpected( e );
1552 }
1553
1554 return Empty();
1555}
1556
1557
1560{
1561 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "GetBoardEditorAppearanceSettings" ) )
1562 return tl::unexpected( *headless );
1563
1564 BoardEditorAppearanceSettings reply;
1565
1566 // TODO: might be nice to put all these things in one place and have it derive SERIALIZABLE
1567
1568 const PCB_DISPLAY_OPTIONS& displayOptions = frame()->GetDisplayOptions();
1569
1570 reply.set_inactive_layer_display( ToProtoEnum<HIGH_CONTRAST_MODE, InactiveLayerDisplayMode>(
1571 displayOptions.m_ContrastModeDisplay ) );
1572 reply.set_net_color_display(
1574
1575 reply.set_board_flip( frame()->GetCanvas()->GetView()->IsMirroredX()
1576 ? BoardFlipMode::BFM_FLIPPED_X
1577 : BoardFlipMode::BFM_NORMAL );
1578
1579 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
1580
1581 reply.set_ratsnest_display( ToProtoEnum<RATSNEST_MODE, RatsnestDisplayMode>(
1582 editorSettings->m_Display.m_RatsnestMode ) );
1583
1584 return reply;
1585}
1586
1587
1590{
1591 if( std::optional<ApiResponseStatus> headless = checkForHeadless( "SetBoardEditorAppearanceSettings" ) )
1592 return tl::unexpected( *headless );
1593
1594 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1595 return tl::unexpected( *busy );
1596
1598 KIGFX::PCB_VIEW* view = frame()->GetCanvas()->GetView();
1599 PCBNEW_SETTINGS* editorSettings = frame()->GetPcbNewSettings();
1600 const BoardEditorAppearanceSettings& newSettings = aCtx.Request.settings();
1601
1602 options.m_ContrastModeDisplay =
1603 FromProtoEnum<HIGH_CONTRAST_MODE>( newSettings.inactive_layer_display() );
1604 options.m_NetColorMode =
1605 FromProtoEnum<NET_COLOR_MODE>( newSettings.net_color_display() );
1606
1607 bool flip = newSettings.board_flip() == BoardFlipMode::BFM_FLIPPED_X;
1608
1609 if( flip != view->IsMirroredX() )
1610 {
1611 view->SetMirror( !view->IsMirroredX(), view->IsMirroredY() );
1612 view->RecacheAllItems();
1613 }
1614
1615 editorSettings->m_Display.m_RatsnestMode =
1616 FromProtoEnum<RATSNEST_MODE>( newSettings.ratsnest_display() );
1617
1618 frame()->SetDisplayOptions( options );
1620 frame()->GetCanvas()->Refresh();
1621
1622 return Empty();
1623}
1624
1625
1628{
1629 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1630 return tl::unexpected( *busy );
1631
1632 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.board() );
1633
1634 if( !documentValidation )
1635 return tl::unexpected( documentValidation.error() );
1636
1637 SEVERITY severity = FromProtoEnum<SEVERITY>( aCtx.Request.severity() );
1638 int layer = severity == RPT_SEVERITY_WARNING ? LAYER_DRC_WARNING : LAYER_DRC_ERROR;
1640
1641 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( code );
1642
1643 drcItem->SetErrorMessage( wxString::FromUTF8( aCtx.Request.message() ) );
1644
1645 RC_ITEM::KIIDS ids;
1646
1647 for( const auto& id : aCtx.Request.items() )
1648 ids.emplace_back( KIID( id.value() ) );
1649
1650 if( !ids.empty() )
1651 drcItem->SetItems( ids );
1652
1653 const auto& pos = aCtx.Request.position();
1654 VECTOR2I position( static_cast<int>( pos.x_nm() ), static_cast<int>( pos.y_nm() ) );
1655
1656 PCB_MARKER* marker = new PCB_MARKER( drcItem, position, layer );
1657
1658 COMMIT* commit = getCurrentCommit( aCtx.ClientName );
1659 commit->Add( marker );
1660 commit->Push( wxS( "API injected DRC marker" ) );
1661
1662 InjectDrcErrorResponse response;
1663 response.mutable_marker()->set_value( marker->GetUUID().AsStdString() );
1664
1665 return response;
1666}
1667
1668
1669std::optional<ApiResponseStatus> ValidateUnitsInchMm( types::Units aUnits,
1670 const std::string& aCommandName )
1671{
1672 if( aUnits == types::Units::U_INCH || aUnits == types::Units::U_MM
1673 || aUnits == types::Units::U_UNKNOWN )
1674 {
1675 return std::nullopt;
1676 }
1677
1678 ApiResponseStatus e;
1679 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1680 e.set_error_message( fmt::format( "{} supports only inch and mm units", aCommandName ) );
1681 return e;
1682}
1683
1684
1685std::optional<ApiResponseStatus>
1686ValidatePaginationModeForSingleOrPerFile( kiapi::board::jobs::BoardJobPaginationMode aMode,
1687 const std::string& aCommandName )
1688{
1689 if( aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_UNKNOWN
1690 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_ALL_LAYERS_ONE_PAGE
1691 || aMode == kiapi::board::jobs::BoardJobPaginationMode::BJPM_EACH_LAYER_OWN_FILE )
1692 {
1693 return std::nullopt;
1694 }
1695
1696 ApiResponseStatus e;
1697 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1698 e.set_error_message( fmt::format( "{} does not support EACH_LAYER_OWN_PAGE pagination mode",
1699 aCommandName ) );
1700 return e;
1701}
1702
1703
1704std::optional<ApiResponseStatus> ApplyBoardPlotSettings( const BoardPlotSettings& aSettings,
1705 JOB_EXPORT_PCB_PLOT& aJob )
1706{
1707 for( int layer : aSettings.layers() )
1708 {
1710 static_cast<board::types::BoardLayer>( layer ) );
1711
1712 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
1713 {
1714 ApiResponseStatus e;
1715 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1716 e.set_error_message( "Board plot settings contain an invalid layer" );
1717 return e;
1718 }
1719
1720 aJob.m_plotLayerSequence.push_back( layerId );
1721 }
1722
1723 for( int layer : aSettings.common_layers() )
1724 {
1726 static_cast<board::types::BoardLayer>( layer ) );
1727
1728 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
1729 {
1730 ApiResponseStatus e;
1731 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1732 e.set_error_message( "Board plot settings contain an invalid common layer" );
1733 return e;
1734 }
1735
1736 aJob.m_plotOnAllLayersSequence.push_back( layerId );
1737 }
1738
1739 aJob.m_colorTheme = wxString::FromUTF8( aSettings.color_theme() );
1740 aJob.m_drawingSheet = wxString::FromUTF8( aSettings.drawing_sheet() );
1741 aJob.m_variant = wxString::FromUTF8( aSettings.variant() );
1742
1743 aJob.m_mirror = aSettings.mirror();
1744 aJob.m_blackAndWhite = aSettings.black_and_white();
1745 aJob.m_negative = aSettings.negative();
1746 aJob.m_scale = aSettings.scale();
1747
1748 aJob.m_sketchPadsOnFabLayers = aSettings.sketch_pads_on_fab_layers();
1749 aJob.m_hideDNPFPsOnFabLayers = aSettings.hide_dnp_footprints_on_fab_layers();
1750 aJob.m_sketchDNPFPsOnFabLayers = aSettings.sketch_dnp_footprints_on_fab_layers();
1751 aJob.m_crossoutDNPFPsOnFabLayers = aSettings.crossout_dnp_footprints_on_fab_layers();
1752
1753 aJob.m_plotFootprintValues = aSettings.plot_footprint_values();
1754 aJob.m_plotRefDes = aSettings.plot_reference_designators();
1755 aJob.m_plotDrawingSheet = aSettings.plot_drawing_sheet();
1756 aJob.m_subtractSolderMaskFromSilk = aSettings.subtract_solder_mask_from_silk();
1757 aJob.m_plotPadNumbers = aSettings.plot_pad_numbers();
1758
1759 aJob.m_drillShapeOption = FromProtoEnum<DRILL_MARKS>( aSettings.drill_marks() );
1760
1761 aJob.m_useDrillOrigin = aSettings.use_drill_origin();
1762 aJob.m_checkZonesBeforePlot = aSettings.check_zones_before_plot();
1763
1764 return std::nullopt;
1765}
1766
1767
1769{
1770 types::RunJobResponse response;
1772
1773 if( !aContext || !aContext->GetKiway() )
1774 {
1775 response.set_status( types::JobStatus::JS_ERROR );
1776 response.set_message( "Internal error" );
1777 return response;
1778 wxCHECK_MSG( false, response, "context missing valid kiway in ExecuteBoardJob?" );
1779 }
1780
1781 int exitCode = aContext->GetKiway()->ProcessJob( KIWAY::FACE_PCB, &aJob, &reporter );
1782
1783 for( const JOB_OUTPUT& output : aJob.GetOutputs() )
1784 response.add_output_path( output.m_outputPath.ToUTF8() );
1785
1786 if( exitCode == 0 )
1787 {
1788 response.set_status( types::JobStatus::JS_SUCCESS );
1789 return response;
1790 }
1791
1792 response.set_status( types::JobStatus::JS_ERROR );
1793 response.set_message( fmt::format( "Board export job '{}' failed with exit code {}: {}",
1794 aJob.GetType(), exitCode,
1795 reporter.GetMessages().ToStdString() ) );
1796 return response;
1797}
1798
1799
1802{
1803 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1804 return tl::unexpected( *busy );
1805
1806 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1807
1808 if( !documentValidation )
1809 return tl::unexpected( documentValidation.error() );
1810
1813 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1814
1816
1817 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
1818 job.m_3dparams.m_NetFilter = wxString::FromUTF8( aCtx.Request.net_filter() );
1819 job.m_3dparams.m_ComponentFilter = wxString::FromUTF8( aCtx.Request.component_filter() );
1820
1821 job.m_hasUserOrigin = aCtx.Request.has_user_origin();
1822 job.m_3dparams.m_Origin = VECTOR2D( aCtx.Request.origin().x_nm(), aCtx.Request.origin().y_nm() );
1823
1824 job.m_3dparams.m_Overwrite = aCtx.Request.overwrite();
1825 job.m_3dparams.m_UseGridOrigin = aCtx.Request.use_grid_origin();
1826 job.m_3dparams.m_UseDrillOrigin = aCtx.Request.use_drill_origin();
1827 job.m_3dparams.m_UseDefinedOrigin = aCtx.Request.use_defined_origin() || aCtx.Request.has_user_origin();
1828 job.m_3dparams.m_UsePcbCenterOrigin = aCtx.Request.use_pcb_center_origin();
1829
1830 job.m_3dparams.m_IncludeUnspecified = aCtx.Request.include_unspecified();
1831 job.m_3dparams.m_IncludeDNP = aCtx.Request.include_dnp();
1832 job.m_3dparams.m_SubstModels = aCtx.Request.substitute_models();
1833
1834 job.m_3dparams.m_BoardOutlinesChainingEpsilon = aCtx.Request.board_outlines_chaining_epsilon();
1835 job.m_3dparams.m_BoardOnly = aCtx.Request.board_only();
1836 job.m_3dparams.m_CutViasInBody = aCtx.Request.cut_vias_in_body();
1837 job.m_3dparams.m_ExportBoardBody = aCtx.Request.export_board_body();
1838 job.m_3dparams.m_ExportComponents = aCtx.Request.export_components();
1839 job.m_3dparams.m_ExportTracksVias = aCtx.Request.export_tracks_and_vias();
1840 job.m_3dparams.m_ExportPads = aCtx.Request.export_pads();
1841 job.m_3dparams.m_ExportZones = aCtx.Request.export_zones();
1842 job.m_3dparams.m_ExportInnerCopper = aCtx.Request.export_inner_copper();
1843 job.m_3dparams.m_ExportSilkscreen = aCtx.Request.export_silkscreen();
1844 job.m_3dparams.m_ExportSoldermask = aCtx.Request.export_soldermask();
1845 job.m_3dparams.m_FuseShapes = aCtx.Request.fuse_shapes();
1846 job.m_3dparams.m_FillAllVias = aCtx.Request.fill_all_vias();
1847 job.m_3dparams.m_OptimizeStep = aCtx.Request.optimize_step();
1848 job.m_3dparams.m_ExtraPadThickness = aCtx.Request.extra_pad_thickness();
1849
1851
1852 job.m_vrmlModelDir = wxString::FromUTF8( aCtx.Request.vrml_model_dir() );
1853 job.m_vrmlRelativePaths = aCtx.Request.vrml_relative_paths();
1854
1855 return ExecuteBoardJob( pcbContext(), job );
1856}
1857
1858
1861{
1862 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1863 return tl::unexpected( *busy );
1864
1865 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1866
1867 if( !documentValidation )
1868 return tl::unexpected( documentValidation.error() );
1869
1870 JOB_PCB_RENDER job;
1872 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1873
1876 job.m_bgStyle = FromProtoEnum<JOB_PCB_RENDER::BG_STYLE>( aCtx.Request.background_style() );
1877
1878 job.m_width = aCtx.Request.width();
1879 job.m_height = aCtx.Request.height();
1880 job.m_appearancePreset = aCtx.Request.appearance_preset();
1881 job.m_useBoardStackupColors = aCtx.Request.use_board_stackup_colors();
1882
1884
1885 job.m_zoom = aCtx.Request.zoom();
1886 job.m_perspective = aCtx.Request.perspective();
1887
1888 job.m_rotation = UnpackVector3D( aCtx.Request.rotation() );
1889 job.m_pan = UnpackVector3D( aCtx.Request.pan() );
1890 job.m_pivot = UnpackVector3D( aCtx.Request.pivot() );
1891
1892 job.m_proceduralTextures = aCtx.Request.procedural_textures();
1893 job.m_floor = aCtx.Request.floor();
1894 job.m_antiAlias = aCtx.Request.anti_alias();
1895 job.m_postProcess = aCtx.Request.post_process();
1896
1897 job.m_lightTopIntensity = UnpackVector3D( aCtx.Request.light_top_intensity() );
1898 job.m_lightBottomIntensity = UnpackVector3D( aCtx.Request.light_bottom_intensity() );
1899 job.m_lightCameraIntensity = UnpackVector3D( aCtx.Request.light_camera_intensity() );
1900 job.m_lightSideIntensity = UnpackVector3D( aCtx.Request.light_side_intensity() );
1901 job.m_lightSideElevation = aCtx.Request.light_side_elevation();
1902
1903 return ExecuteBoardJob( pcbContext(), job );
1904}
1905
1906
1909{
1910 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1911 return tl::unexpected( *busy );
1912
1913 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1914
1915 if( !documentValidation )
1916 return tl::unexpected( documentValidation.error() );
1917
1920 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1921
1922 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
1923 return tl::unexpected( *err );
1924
1925 job.m_fitPageToBoard = aCtx.Request.fit_page_to_board();
1926 job.m_precision = aCtx.Request.precision();
1927
1928 if( std::optional<ApiResponseStatus> paginationError =
1930 "RunBoardJobExportSvg" ) )
1931 {
1932 return tl::unexpected( *paginationError );
1933 }
1934
1936
1937 return ExecuteBoardJob( pcbContext(), job );
1938}
1939
1940
1943{
1944 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1945 return tl::unexpected( *busy );
1946
1947 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1948
1949 if( !documentValidation )
1950 return tl::unexpected( documentValidation.error() );
1951
1954 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1955
1956 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
1957 return tl::unexpected( *err );
1958
1959 job.m_plotGraphicItemsUsingContours = aCtx.Request.plot_graphic_items_using_contours();
1960 job.m_polygonMode = aCtx.Request.polygon_mode();
1961
1962 if( std::optional<ApiResponseStatus> unitError =
1963 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDxf" ) )
1964 {
1965 return tl::unexpected( *unitError );
1966 }
1967
1969
1970 if( std::optional<ApiResponseStatus> paginationError =
1972 "RunBoardJobExportDxf" ) )
1973 {
1974 return tl::unexpected( *paginationError );
1975 }
1976
1978
1979 return ExecuteBoardJob( pcbContext(), job );
1980}
1981
1982
1985{
1986 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1987 return tl::unexpected( *busy );
1988
1989 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1990
1991 if( !documentValidation )
1992 return tl::unexpected( documentValidation.error() );
1993
1996 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1997
1998 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
1999 return tl::unexpected( *err );
2000
2001 job.m_pdfFrontFPPropertyPopups = aCtx.Request.front_footprint_property_popups();
2002 job.m_pdfBackFPPropertyPopups = aCtx.Request.back_footprint_property_popups();
2003 job.m_pdfMetadata = aCtx.Request.include_metadata();
2004 job.m_pdfSingle = aCtx.Request.single_document();
2005 job.m_pdfBackgroundColor = wxString::FromUTF8( aCtx.Request.background_color() );
2006
2008
2009 return ExecuteBoardJob( pcbContext(), job );
2010}
2011
2012
2015{
2016 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2017 return tl::unexpected( *busy );
2018
2019 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2020
2021 if( !documentValidation )
2022 return tl::unexpected( documentValidation.error() );
2023
2026 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2027
2028 if( std::optional<ApiResponseStatus> err = ApplyBoardPlotSettings( aCtx.Request.plot_settings(), job ) )
2029 return tl::unexpected( *err );
2030
2031 if( std::optional<ApiResponseStatus> paginationError =
2033 "RunBoardJobExportPs" ) )
2034 {
2035 return tl::unexpected( *paginationError );
2036 }
2037
2039
2040 job.m_trackWidthCorrection = aCtx.Request.track_width_correction();
2041 job.m_XScaleAdjust = aCtx.Request.x_scale_adjust();
2042 job.m_YScaleAdjust = aCtx.Request.y_scale_adjust();
2043 job.m_forceA4 = aCtx.Request.force_a4();
2044 job.m_useGlobalSettings = aCtx.Request.use_global_settings();
2045
2046 return ExecuteBoardJob( pcbContext(), job );
2047}
2048
2049
2052{
2053 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2054 return tl::unexpected( *busy );
2055
2056 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2057
2058 if( !documentValidation )
2059 return tl::unexpected( documentValidation.error() );
2060
2061 if( aCtx.Request.layers().empty() )
2062 {
2063 ApiResponseStatus e;
2064 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2065 e.set_error_message( "RunBoardJobExportGerbers requires at least one layer" );
2066 return tl::unexpected( e );
2067 }
2068
2071 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2072
2073 for( int layer : aCtx.Request.layers() )
2074 {
2075 PCB_LAYER_ID layerId =
2077 static_cast<board::types::BoardLayer>( layer ) );
2078
2079 if( layerId == PCB_LAYER_ID::UNDEFINED_LAYER )
2080 {
2081 ApiResponseStatus e;
2082 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
2083 e.set_error_message( "RunBoardJobExportGerbers contains an invalid layer" );
2084 return tl::unexpected( e );
2085 }
2086
2087 job.m_plotLayerSequence.push_back( layerId );
2088 }
2089
2090 return ExecuteBoardJob( pcbContext(), job );
2091}
2092
2093
2096{
2097 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2098 return tl::unexpected( *busy );
2099
2100 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2101
2102 if( !documentValidation )
2103 return tl::unexpected( documentValidation.error() );
2104
2107 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2108
2110
2111 if( std::optional<ApiResponseStatus> unitError =
2112 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportDrill" ) )
2113 {
2114 return tl::unexpected( *unitError );
2115 }
2116
2120
2121 if( aCtx.Request.has_excellon() )
2122 {
2123 const ExcellonFormatOptions& excellonOptions = aCtx.Request.excellon();
2124
2125 if( excellonOptions.has_mirror_y() )
2126 job.m_excellonMirrorY = excellonOptions.mirror_y();
2127
2128 if( excellonOptions.has_minimal_header() )
2129 job.m_excellonMinimalHeader = excellonOptions.minimal_header();
2130
2131 if( excellonOptions.has_combine_pth_npth() )
2132 job.m_excellonCombinePTHNPTH = excellonOptions.combine_pth_npth();
2133
2134 if( excellonOptions.has_route_oval_holes() )
2135 job.m_excellonOvalDrillRoute = excellonOptions.route_oval_holes();
2136 }
2137
2138 if( aCtx.Request.map_format() != DrillMapFormat::DMF_UNKNOWN )
2139 {
2140 job.m_generateMap = true;
2142 }
2143
2144 job.m_gerberPrecision = aCtx.Request.gerber_precision() == DrillGerberPrecision::DGP_4_5 ? 5 : 6;
2145
2146 if( aCtx.Request.has_gerber_generate_tenting() )
2147 job.m_generateTenting = aCtx.Request.gerber_generate_tenting();
2148
2149 if( aCtx.Request.report_format() != DrillReportFormat::DRF_UNKNOWN )
2150 {
2151 job.m_generateReport = true;
2152
2153 if( aCtx.Request.has_report_filename() )
2154 job.m_reportPath = wxString::FromUTF8( aCtx.Request.report_filename() );
2155 }
2156
2157 return ExecuteBoardJob( pcbContext(), job );
2158}
2159
2160
2163{
2164 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2165 return tl::unexpected( *busy );
2166
2167 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2168
2169 if( !documentValidation )
2170 return tl::unexpected( documentValidation.error() );
2171
2174 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2175
2176 if( aCtx.Request.has_use_drill_place_file_origin() )
2177 job.m_useDrillPlaceFileOrigin = aCtx.Request.use_drill_place_file_origin();
2178
2179 job.m_smdOnly = aCtx.Request.smd_only();
2180 job.m_excludeFootprintsWithTh = aCtx.Request.exclude_footprints_with_th();
2181 job.m_excludeDNP = aCtx.Request.exclude_dnp();
2182 job.m_excludeBOM = aCtx.Request.exclude_from_bom();
2183 job.m_negateBottomX = aCtx.Request.negate_bottom_x();
2184 job.m_singleFile = aCtx.Request.single_file();
2185 job.m_nakedFilename = aCtx.Request.naked_filename();
2186 if( aCtx.Request.has_include_board_edge_for_gerber() )
2187 job.m_gerberBoardEdge = aCtx.Request.include_board_edge_for_gerber();
2188
2189 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2190
2192
2193 if( std::optional<ApiResponseStatus> unitError =
2194 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportPosition" ) )
2195 {
2196 return tl::unexpected( *unitError );
2197 }
2198
2201
2202 return ExecuteBoardJob( pcbContext(), job );
2203}
2204
2205
2208{
2209 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2210 return tl::unexpected( *busy );
2211
2212 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2213
2214 if( !documentValidation )
2215 return tl::unexpected( documentValidation.error() );
2216
2219 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2220
2221 job.m_flipBottomPads = aCtx.Request.flip_bottom_pads();
2222 job.m_useIndividualShapes = aCtx.Request.use_individual_shapes();
2223 job.m_storeOriginCoords = aCtx.Request.store_origin_coords();
2224 job.m_useDrillOrigin = aCtx.Request.use_drill_origin();
2225 job.m_useUniquePins = aCtx.Request.use_unique_pins();
2226
2227 return ExecuteBoardJob( pcbContext(), job );
2228}
2229
2230
2233{
2234 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2235 return tl::unexpected( *busy );
2236
2237 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2238
2239 if( !documentValidation )
2240 return tl::unexpected( documentValidation.error() );
2241
2244 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2245
2246 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
2247 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2248 if( aCtx.Request.has_precision() )
2249 job.m_precision = aCtx.Request.precision();
2250
2251 job.m_compress = aCtx.Request.compress();
2252 job.m_colInternalId = wxString::FromUTF8( aCtx.Request.internal_id_column() );
2253 job.m_colMfgPn = wxString::FromUTF8( aCtx.Request.manufacturer_part_number_column() );
2254 job.m_colMfg = wxString::FromUTF8( aCtx.Request.manufacturer_column() );
2255 job.m_colDistPn = wxString::FromUTF8( aCtx.Request.distributor_part_number_column() );
2256 job.m_colDist = wxString::FromUTF8( aCtx.Request.distributor_column() );
2257 job.m_bomRev = wxString::FromUTF8( aCtx.Request.bom_revision() );
2258
2259 if( std::optional<ApiResponseStatus> unitError =
2260 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportIpc2581" ) )
2261 {
2262 return tl::unexpected( *unitError );
2263 }
2264
2267
2268 return ExecuteBoardJob( pcbContext(), job );
2269}
2270
2271
2274{
2275 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2276 return tl::unexpected( *busy );
2277
2278 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2279
2280 if( !documentValidation )
2281 return tl::unexpected( documentValidation.error() );
2282
2285 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2286
2287 return ExecuteBoardJob( pcbContext(), job );
2288}
2289
2290
2293{
2294 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2295 return tl::unexpected( *busy );
2296
2297 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2298
2299 if( !documentValidation )
2300 return tl::unexpected( documentValidation.error() );
2301
2304 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2305
2306 job.m_drawingSheet = wxString::FromUTF8( aCtx.Request.drawing_sheet() );
2307 job.m_variant = wxString::FromUTF8( aCtx.Request.variant() );
2308 if( aCtx.Request.has_precision() )
2309 job.m_precision = aCtx.Request.precision();
2310
2311 if( std::optional<ApiResponseStatus> unitError =
2312 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportODB" ) )
2313 {
2314 return tl::unexpected( *unitError );
2315 }
2316
2319
2320 return ExecuteBoardJob( pcbContext(), job );
2321}
2322
2323
2326{
2327 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
2328 return tl::unexpected( *busy );
2329
2330 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
2331
2332 if( !documentValidation )
2333 return tl::unexpected( documentValidation.error() );
2334
2337 job.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
2338
2340
2341 if( std::optional<ApiResponseStatus> unitError =
2342 ValidateUnitsInchMm( aCtx.Request.units(), "RunBoardJobExportStats" ) )
2343 {
2344 return tl::unexpected( *unitError );
2345 }
2346
2348
2349 job.m_excludeFootprintsWithoutPads = aCtx.Request.exclude_footprints_without_pads();
2350 job.m_subtractHolesFromBoardArea = aCtx.Request.subtract_holes_from_board_area();
2351 job.m_subtractHolesFromCopperAreas = aCtx.Request.subtract_holes_from_copper_areas();
2352
2353 return ExecuteBoardJob( pcbContext(), job );
2354}
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.
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION gridSetOrigin
Definition actions.h:195
API_HANDLER_BOARD(std::shared_ptr< BOARD_CONTEXT > aContext, EDA_BASE_FRAME *aFrame=nullptr)
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
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:85
void SetContentModified(bool aModified=true)
Definition base_screen.h:59
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
virtual PROJECT & Prj() 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:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const PAGE_INFO & GetPageSettings() const
Definition board.h:807
void SetDesignSettings(const BOARD_DESIGN_SETTINGS &aSettings)
Definition board.cpp:1107
TITLE_BLOCK & GetTitleBlock()
Definition board.h:813
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition board.h:808
const wxString & GetFileName() const
Definition board.h:360
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1101
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:571
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:72
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:78
void ToProto(kiapi::board::CustomRuleConstraint &aProto) const
Definition drc_rule.cpp:378
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:416
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
static wxString ExtractRuleComment(const wxString &aOriginalText)
Extract comment lines from a rule.
static wxString ExtractRuleText(const wxString &aContent, const wxString &aRuleName)
Extract the complete original text of a rule from file content.
static wxString FormatRuleFromProto(const kiapi::board::CustomRule &aRule, wxString *aErrorText=nullptr)
Definition drc_rule.cpp:84
void ReleaseFile()
Release the current file marked in use.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
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:163
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:628
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition view.cpp:848
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition view.h:259
void RecacheAllItems()
Rebuild GAL display lists.
Definition view.cpp:1551
bool IsMirroredY() const
Return true if view is flipped across the Y axis.
Definition view.h:267
Definition kiid.h:48
std::string AsStdString() const
Definition kiid.cpp:250
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:750
@ FACE_PCB
pcbnew DSO
Definition kiway.h:323
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static int NameToLayer(wxString &aName)
Return the layer number from a layer name.
Definition lset.cpp:117
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:42
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:284
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition netclass.cpp:136
Handle the data for a net.
Definition netinfo.h:50
NETCLASS * GetNetClass()
Definition netinfo.h:95
int GetNetCode() const
Definition netinfo.h:98
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:225
NETINFO_ITEM * GetNetItem(int aNetCode) const
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
DISPLAY_OPTIONS m_Display
static TOOL_ACTION zoneFillAll
static TOOL_ACTION 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:35
virtual bool SaveBoard()=0
virtual wxString GetCurrentFileName() const =0
virtual bool SavePcbCopy(const wxString &aFileName, bool aCreateProject, bool aHeadless)=0
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:49
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition project.cpp:411
std::vector< KIID > KIIDS
Definition rc_item.h:86
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.
A wrapper for reporting to a wxString object.
Definition reporter.h:193
A type-safe container of any type.
Definition ki_any.h:93
The common library.
PCB_DRC_CODE
Definition drc_item.h:38
@ DRCE_GENERIC_ERROR
Definition drc_item.h:91
@ DRCE_GENERIC_WARNING
Definition drc_item.h:90
static const std::string DesignRulesFileExtension
static const std::string KiCadPcbFileExtension
#define KICTL_REVERT
reverting to a previously-saved (KiCad) file.
#define MAX_CU_LAYERS
Definition layer_ids.h:176
@ LAYER_DRC_WARNING
Layer for DRC markers with #SEVERITY_WARNING.
Definition layer_ids.h:301
@ LAYER_DRC_ERROR
Layer for DRC markers with #SEVERITY_ERROR.
Definition layer_ids.h:277
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ Edge_Cuts
Definition layer_ids.h:112
@ UNSELECTED_LAYER
Definition layer_ids.h:62
@ Margin
Definition layer_ids.h:113
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
KICOMMON_API 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
IbisParser parser & reporter
nlohmann::json output
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:75
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:103
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:100
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:101
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:108
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:90
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:105
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:89
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:86
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:98
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:99
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:97
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:102
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686