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