KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_handler_sch.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) 2024 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 <api/api_handler_sch.h>
22#include <api/api_enums.h>
23#include <api/api_sch_utils.h>
24#include <api/api_utils.h>
25#include <api/sch_context.h>
26#include <magic_enum.hpp>
27#include <base_screen.h>
31#include <kiway.h>
32#include <sch_field.h>
33#include <sch_group.h>
34#include <connection_graph.h>
35#include <sch_commit.h>
36#include <sch_edit_frame.h>
37#include <sch_label.h>
38#include <sch_screen.h>
39#include <sch_sheet.h>
40#include <sch_sheet_path.h>
41#include <sch_sheet_pin.h>
42#include <sch_symbol.h>
43#include <schematic.h>
44#include <project.h>
46#include <wx/filename.h>
47
48#include <api/common/types/base_types.pb.h>
49
50using namespace kiapi::common::commands;
51using kiapi::common::types::CommandStatus;
52using kiapi::common::types::DocumentType;
53using kiapi::common::types::ItemRequestStatus;
54
55
56std::set<KICAD_T> API_HANDLER_SCH::s_allowedTypes = {
57 // SCH_MARKER_T,
67 // SCH_TABLE_T,
75};
76
77
79{
80 types::RunJobResponse response;
82 int exitCode = aKiway->ProcessJob( KIWAY::FACE_SCH, &aJob, &reporter );
83
84 for( const JOB_OUTPUT& output : aJob.GetOutputs() )
85 response.add_output_path( output.m_outputPath.ToUTF8() );
86
87 if( exitCode == 0 )
88 {
89 response.set_status( types::JobStatus::JS_SUCCESS );
90 return response;
91 }
92
93 response.set_status( types::JobStatus::JS_ERROR );
94 response.set_message( fmt::format( "Schematic export job '{}' failed with exit code {}: {}",
95 aJob.GetType(), exitCode,
96 reporter.GetMessages().ToStdString() ) );
97 return response;
98}
99
100
105
106
107API_HANDLER_SCH::API_HANDLER_SCH( std::shared_ptr<SCH_CONTEXT> aContext,
108 SCH_EDIT_FRAME* aFrame ) :
109 API_HANDLER_EDITOR( aFrame ),
110 m_frame( aFrame ),
111 m_context( std::move( aContext ) )
112{
113 using namespace kiapi::schematic::jobs;
114 using namespace kiapi::schematic::types;
115
122
125
142}
143
144
145std::unique_ptr<COMMIT> API_HANDLER_SCH::createCommit()
146{
147 if( m_frame )
148 return std::make_unique<SCH_COMMIT>( m_frame );
149
150 return std::make_unique<SCH_COMMIT>( toolManager() );
151}
152
153
155{
156 wxCHECK( m_context, nullptr );
157 return m_context->GetSchematic();
158}
159
160
161std::optional<SCH_ITEM*> API_HANDLER_SCH::getItemById( const KIID& aId, SCH_SHEET_PATH* aPathOut ) const
162{
163 if( !schematic()->HasHierarchy() )
165
166 SCH_ITEM* item = schematic()->ResolveItem( aId, aPathOut, true );
167
168 if( !item )
169 return std::nullopt;
170
171 return item;
172}
173
174
175tl::expected<bool, ApiResponseStatus>
176API_HANDLER_SCH::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
177{
178 if( aDocument.type() != DocumentType::DOCTYPE_SCHEMATIC )
179 {
180 ApiResponseStatus e;
181 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
182 e.set_error_message( "the requested document is not a schematic" );
183 return tl::unexpected( e );
184 }
185
186 const PROJECT& prj = m_context->Prj();
187
188 if( aDocument.project().name().compare( prj.GetProjectName().ToUTF8() ) != 0 )
189 {
190 ApiResponseStatus e;
191 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
192 e.set_error_message( fmt::format( "the requested project {} is not open",
193 aDocument.project().name() ) );
194 return tl::unexpected( e );
195 }
196
197 if( aDocument.project().path().compare( prj.GetProjectPath().ToUTF8() ) != 0 )
198 {
199 ApiResponseStatus e;
200 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
201 e.set_error_message( fmt::format( "the requested project {} is not open at path {}",
202 aDocument.project().name(),
203 aDocument.project().path() ) );
204 return tl::unexpected( e );
205 }
206
207 if( aDocument.has_sheet_path() )
208 {
209 KIID_PATH path = UnpackSheetPath( aDocument.sheet_path() );
210
211 if( !schematic()->Hierarchy().HasPath( path ) )
212 {
213 ApiResponseStatus e;
214 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
215 e.set_error_message( fmt::format( "the requested sheet path {} is not valid for this schematic",
216 path.AsString().ToStdString() ) );
217 return tl::unexpected( e );
218 }
219 }
220
221 return true;
222}
223
224
226{
227 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
228 return tl::unexpected( *busy );
229
230 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
231
232 if( !documentValidation )
233 return tl::unexpected( documentValidation.error() );
234
235 if( !context()->SaveSchematic() )
236 {
237 ApiResponseStatus e;
238 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
239 e.set_error_message( "failed to save schematic" );
240 return tl::unexpected( e );
241 }
242
243 return google::protobuf::Empty();
244}
245
246
249{
250 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
251 return tl::unexpected( *busy );
252
253 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
254
255 if( !documentValidation )
256 return tl::unexpected( documentValidation.error() );
257
258 wxFileName schematicPath( project().AbsolutePath( wxString::FromUTF8( aCtx.Request.path() ) ) );
259
260 if( !schematicPath.IsOk() || !schematicPath.IsDirWritable() )
261 {
262 ApiResponseStatus e;
263 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
264 e.set_error_message(
265 fmt::format( "save path '{}' could not be opened", schematicPath.GetFullPath().ToStdString() ) );
266 return tl::unexpected( e );
267 }
268
269 if( schematicPath.FileExists() && ( !schematicPath.IsFileWritable() || !aCtx.Request.options().overwrite() ) )
270 {
271 ApiResponseStatus e;
272 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
273 e.set_error_message( fmt::format( "save path '{}' exists and cannot be overwritten",
274 schematicPath.GetFullPath().ToStdString() ) );
275 return tl::unexpected( e );
276 }
277
278 if( schematicPath.GetExt() != FILEEXT::KiCadSchematicFileExtension )
279 {
280 ApiResponseStatus e;
281 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
282 e.set_error_message( fmt::format( "save path '{}' must have a kicad_sch extension",
283 schematicPath.GetFullPath().ToStdString() ) );
284 return tl::unexpected( e );
285 }
286
287 bool includeProject = true;
288
289 if( aCtx.Request.has_options() )
290 includeProject = aCtx.Request.options().include_project();
291
292 if( !context()->SaveSchematicCopy( schematicPath.GetFullPath(), includeProject ) )
293 {
294 ApiResponseStatus e;
295 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
296 e.set_error_message( "failed to save schematic copy" );
297 return tl::unexpected( e );
298 }
299
300 return google::protobuf::Empty();
301}
302
303
306{
307 if( aCtx.Request.type() != DocumentType::DOCTYPE_SCHEMATIC )
308 {
309 ApiResponseStatus e;
310
311 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
312 e.set_status( ApiStatusCode::AS_UNHANDLED );
313 return tl::unexpected( e );
314 }
315
316 GetOpenDocumentsResponse response;
317 common::types::DocumentSpecifier doc;
318
319 wxFileName fn( m_context->GetCurrentFileName() );
320
321 doc.set_type( DocumentType::DOCTYPE_SCHEMATIC );
322
323 if( std::optional<SCH_SHEET_PATH> path = m_context->GetCurrentSheet() )
324 PackSheetPath( *doc.mutable_sheet_path(), path->Path() );
325
326 PackProject( *doc.mutable_project(), m_context->Prj() );
327
328 response.mutable_documents()->Add( std::move( doc ) );
329 return response;
330}
331
332
333void API_HANDLER_SCH::filterValidSchTypes( std::set<KICAD_T>& aTypeList )
334{
335 std::erase_if( aTypeList,
336 []( KICAD_T aType )
337 {
338 return !s_allowedTypes.contains( aType );
339 } );
340}
341
342
344{
345 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
346 return tl::unexpected( *busy );
347
348 if( HANDLER_RESULT<std::optional<KIID>> valid = validateItemHeaderDocument( aCtx.Request.header() );
349 !valid.has_value() )
350 {
351 return tl::unexpected( valid.error() );
352 }
353
354 std::set<KICAD_T> typesRequested, typesInserted;
355
356 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
357 typesRequested.insert( type );
358
359 filterValidSchTypes( typesRequested );
360
361 if( typesRequested.empty() )
362 {
363 ApiResponseStatus e;
364 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
365 e.set_error_message( "none of the requested types are valid for a Schematic object" );
366 return tl::unexpected( e );
367 }
368
369 SCH_SHEET_LIST hierarchy = schematic()->Hierarchy();
370 std::optional<SCH_SHEET_PATH> pathFilter;
371
372 if( aCtx.Request.header().document().has_sheet_path() )
373 {
374 KIID_PATH kp = UnpackSheetPath( aCtx.Request.header().document().sheet_path() );
375 pathFilter = hierarchy.GetSheetPathByKIIDPath( kp );
376 }
377
378 std::map<KICAD_T, std::vector<std::pair<EDA_ITEM*, SCH_SHEET_PATH>>> itemMap;
379
380 auto processScreen =
381 [&]( const SCH_SHEET_PATH& aPath )
382 {
383 const SCH_SCREEN* aScreen = aPath.LastScreen();
384
385 for( SCH_ITEM* aItem : aScreen->Items() )
386 {
387 itemMap[ aItem->Type() ].emplace_back( aItem, aPath );
388
389 aItem->RunOnChildren(
390 [&]( SCH_ITEM* aChild )
391 {
392 itemMap[ aChild->Type() ].emplace_back( aChild, aPath );
393 },
395 }
396 };
397
398 if( pathFilter )
399 {
400 processScreen( *pathFilter );
401 }
402 else
403 {
404 for( const SCH_SHEET_PATH& path : hierarchy )
405 processScreen( path );
406 }
407
408 GetItemsResponse response;
409 google::protobuf::Any any;
410
411 for( KICAD_T type : parseRequestedItemTypes( aCtx.Request.types() ) )
412 {
413 if( !s_allowedTypes.contains( type ) )
414 continue;
415
416 if( typesInserted.contains( type ) )
417 continue;
418
419 for( const auto& [item, itemPath] : itemMap[type] )
420 {
421 if( item->Type() == SCH_SYMBOL_T )
422 {
423 kiapi::schematic::types::SchematicSymbolInstance symbol;
424
425 if( !PackSymbol( &symbol, static_cast<SCH_SYMBOL*>( item ), itemPath ) )
426 continue;
427
428 any.PackFrom( symbol );
429 }
430 else if( item->Type() == SCH_SHEET_T )
431 {
432 kiapi::schematic::types::SheetSymbol sheet;
433
434 if( !PackSheet( &sheet, static_cast<SCH_SHEET*>( item ), itemPath ) )
435 continue;
436
437 any.PackFrom( sheet );
438 }
439 else
440 {
441 item->Serialize( any );
442 }
443
444 response.mutable_items()->Add( std::move( any ) );
445 }
446 }
447
448 response.set_status( ItemRequestStatus::IRS_OK );
449 return response;
450}
451
452
454{
455 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
456 return tl::unexpected( *busy );
457
458 if( !validateItemHeaderDocument( aCtx.Request.header() ) )
459 {
460 ApiResponseStatus e;
461 e.set_status( ApiStatusCode::AS_UNHANDLED );
462 return tl::unexpected( e );
463 }
464
465 SCH_SHEET_LIST hierarchy = schematic()->Hierarchy();
466 std::optional<SCH_SHEET_PATH> pathFilter;
467
468 if( aCtx.Request.header().document().has_sheet_path() )
469 {
470 KIID_PATH kp = UnpackSheetPath( aCtx.Request.header().document().sheet_path() );
471 pathFilter = hierarchy.GetSheetPathByKIIDPath( kp );
472 }
473
474 GetItemsResponse response;
475 SCH_ITEM* item = nullptr;
476 google::protobuf::Any any;
477
478 for( const types::KIID& idProto : aCtx.Request.items() )
479 {
480 KIID id( idProto.value() );
481
482 SCH_SHEET_PATH itemPath;
483
484 if( pathFilter )
485 {
486 item = pathFilter->ResolveItem( id );
487 itemPath = *pathFilter;
488 }
489 else
490 {
491 item = hierarchy.ResolveItem( id, &itemPath, true );
492 }
493
494 if( !item || !s_allowedTypes.contains( item->Type() ) )
495 continue;
496
497 if( item->Type() == SCH_SYMBOL_T )
498 {
499 kiapi::schematic::types::SchematicSymbolInstance symbol;
500
501 if( !PackSymbol( &symbol, static_cast<SCH_SYMBOL*>( item ), itemPath ) )
502 continue;
503
504 any.PackFrom( symbol );
505 }
506 else if( item->Type() == SCH_SHEET_T )
507 {
508 kiapi::schematic::types::SheetSymbol sheet;
509
510 if( !PackSheet( &sheet, static_cast<SCH_SHEET*>( item ), itemPath ) )
511 continue;
512
513 any.PackFrom( sheet );
514 }
515 else
516 {
517 item->Serialize( any );
518 }
519
520 response.mutable_items()->Add( std::move( any ) );
521 }
522
523 if( response.items().empty() )
524 {
525 ApiResponseStatus e;
526 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
527 e.set_error_message( "none of the requested IDs were found or valid" );
528 return tl::unexpected( e );
529 }
530
531 response.set_status( ItemRequestStatus::IRS_OK );
532 return response;
533}
534
535
537{
538 if( !aContainer )
539 {
540 ApiResponseStatus e;
541 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
542 e.set_error_message( "Tried to create an item in a null container" );
543 return tl::unexpected( e );
544 }
545
546 if( !s_allowedTypes.contains( aType ) )
547 {
548 ApiResponseStatus e;
549 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
550 e.set_error_message( fmt::format( "type {} is not supported by the schematic API handler",
551 magic_enum::enum_name( aType ) ) );
552 return tl::unexpected( e );
553 }
554
555 if( aType == SCH_PIN_T && !dynamic_cast<SCH_SYMBOL*>( aContainer ) )
556 {
557 ApiResponseStatus e;
558 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
559 e.set_error_message( fmt::format( "Tried to create a pin in {}, which is not a symbol",
560 aContainer->GetFriendlyName().ToStdString() ) );
561 return tl::unexpected( e );
562 }
563 else if( aType == SCH_SHEET_T && !dynamic_cast<SCH_SCREEN*>( aContainer ) )
564 {
565 ApiResponseStatus e;
566 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
567 e.set_error_message( fmt::format( "Tried to create a sheet symbol in {}, which is not a "
568 "schematic sheet",
569 aContainer->GetFriendlyName().ToStdString() ) );
570 return tl::unexpected( e );
571 }
572 else if( aType == SCH_SYMBOL_T && !dynamic_cast<SCH_SCREEN*>( aContainer ) )
573 {
574 ApiResponseStatus e;
575 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
576 e.set_error_message( fmt::format( "Tried to create a symbol in {}, which is not a "
577 "schematic sheet",
578 aContainer->GetFriendlyName().ToStdString() ) );
579 return tl::unexpected( e );
580 }
581
582 std::unique_ptr<EDA_ITEM> created = CreateItemForType( aType, aContainer );
583
584 if( created && !created->GetParent() )
585 created->SetParent( aContainer );
586
587 if( !created )
588 {
589 ApiResponseStatus e;
590 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
591 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
592 magic_enum::enum_name( aType ) ) );
593 return tl::unexpected( e );
594 }
595
596 return created;
597}
598
599
601 const std::string& aClientName,
602 const types::ItemHeader &aHeader,
603 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
604 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
605{
606 ApiResponseStatus e;
607
608 auto containerResult = validateItemHeaderDocument( aHeader );
609
610 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
611 {
612 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
613 e.set_status( ApiStatusCode::AS_UNHANDLED );
614 return tl::unexpected( e );
615 }
616 else if( !containerResult )
617 {
618 e.CopyFrom( containerResult.error() );
619 return tl::unexpected( e );
620 }
621
622 SCH_SHEET_LIST hierarchy = schematic()->Hierarchy();
623 SCH_SCREEN* targetScreen = schematic()->GetCurrentScreen();
624 SCH_SHEET_PATH targetPath = m_context->GetCurrentSheet().value_or( *hierarchy.begin() );
625
626 if( aHeader.document().has_sheet_path() )
627 {
628 KIID_PATH kp = UnpackSheetPath( aHeader.document().sheet_path() );
629 if( std::optional<SCH_SHEET_PATH> path = hierarchy.GetSheetPathByKIIDPath( kp ) )
630 {
631 targetPath = *path;
632 targetScreen = targetPath.LastScreen();
633 }
634 }
635
636 SCH_COMMIT* commit = static_cast<SCH_COMMIT*>( getCurrentCommit( aClientName ) );
637
638 for( const google::protobuf::Any& anyItem : aItems )
639 {
640 ItemStatus status;
641 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
642
643 if( !type )
644 {
645 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
646 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
647 anyItem.type_url() ) );
648 aItemHandler( status, anyItem );
649 continue;
650 }
651
652 EDA_ITEM* container = targetScreen;
653
654 HANDLER_RESULT<std::unique_ptr<EDA_ITEM>> creationResult = createItemForType( *type, container );
655
656 if( !creationResult )
657 {
658 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
659 status.set_error_message( creationResult.error().error_message() );
660 aItemHandler( status, anyItem );
661 continue;
662 }
663
664 std::unique_ptr<EDA_ITEM> item( std::move( *creationResult ) );
665
666 bool unpacked = false;
667
668 if( *type == SCH_SYMBOL_T )
669 {
670 kiapi::schematic::types::SchematicSymbolInstance symbol;
671 unpacked = anyItem.UnpackTo( &symbol )
672 && UnpackSymbol( static_cast<SCH_SYMBOL*>( item.get() ), symbol );
673 }
674 else if( *type == SCH_SHEET_T )
675 {
676 kiapi::schematic::types::SheetSymbol sheetProto;
677 unpacked = anyItem.UnpackTo( &sheetProto );
678
679 if( unpacked )
680 {
681 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item.get() );
682
683 if( tl::expected<bool, ApiResponseStatus> result = UnpackSheet( sheet, sheetProto );
684 result.has_value() )
685 {
686 unpacked = *result;
687 SCH_SHEET_INSTANCE instance;
688
689 if( !sheet->GetInstances().empty() )
690 instance = *sheet->GetInstances().begin();
691
692 if( instance.m_PageNumber.IsEmpty() )
693 instance.m_PageNumber = hierarchy.GetNextPageNumber();
694
695 if( instance.m_Path.empty() )
696 {
697 SCH_SHEET_PATH newPath( targetPath );
698 newPath.push_back( sheet );
699 instance.m_Path = newPath.Path();
700 }
701
702 sheet->AddInstance( instance );
703 }
704 else
705 {
706 return tl::unexpected( result.error() );
707 }
708 }
709 }
710 else
711 {
712 unpacked = item->Deserialize( anyItem );
713 }
714
715 if( !unpacked )
716 {
717 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
718 e.set_error_message( fmt::format( "could not unpack {} from request",
719 item->GetClass().ToStdString() ) );
720 return tl::unexpected( e );
721 }
722
723 SCH_ITEM* existingItem = nullptr;
724 SCH_SHEET_PATH existingPath;
725
726 existingItem = targetPath.ResolveItem( item->m_Uuid );
727
728 if( existingItem )
729 existingPath = targetPath;
730
731 if( aCreate && existingItem )
732 {
733 status.set_code( ItemStatusCode::ISC_EXISTING );
734 status.set_error_message( fmt::format( "an item with UUID {} already exists",
735 item->m_Uuid.AsStdString() ) );
736 aItemHandler( status, anyItem );
737 continue;
738 }
739 else if( !aCreate && !existingItem )
740 {
741 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
742 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
743 item->m_Uuid.AsStdString() ) );
744 aItemHandler( status, anyItem );
745 continue;
746 }
747
748 if( !aCreate )
749 {
750 SCH_SCREEN* itemScreen = existingPath.LastScreen();
751
752 if( itemScreen != targetScreen )
753 {
754 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
755 status.set_error_message( fmt::format( "item {} exists on a different sheet than targeted",
756 item->m_Uuid.AsStdString() ) );
757 aItemHandler( status, anyItem );
758 continue;
759 }
760 }
761
762 if( *type == SCH_SHEET_T )
763 {
764 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item.get() );
765
766 if( aCreate && !sheet->GetScreen() )
767 sheet->SetScreen( new SCH_SCREEN( schematic() ) );
768
769 SCH_SHEET_PATH parentPath;
770
771 if( aCreate )
772 parentPath = targetPath;
773 else
774 parentPath = existingPath;
775
776 wxString destFilePath = parentPath.LastScreen()->GetFileName();
777
778 if( !destFilePath.IsEmpty() )
779 {
780 SCH_SHEET_LIST schematicSheets = schematic()->Hierarchy();
781 SCH_SHEET_LIST loadedSheets( sheet );
782
783 if( schematicSheets.TestForRecursion( loadedSheets, destFilePath ) )
784 {
785 status.set_code( ItemStatusCode::ISC_INVALID_DATA );
786 status.set_error_message( "sheet update would create recursive hierarchy" );
787 aItemHandler( status, anyItem );
788 continue;
789 }
790 }
791 }
792
793 status.set_code( ItemStatusCode::ISC_OK );
794 google::protobuf::Any newItem;
795
796 if( aCreate )
797 {
798 SCH_ITEM* createdItem = static_cast<SCH_ITEM*>( item.release() );
799 commit->Add( createdItem, targetScreen );
800
801 if( !createdItem )
802 {
803 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
804 e.set_error_message( "could not add the requested item to its parent container" );
805 return tl::unexpected( e );
806 }
807
808 if( createdItem->Type() == SCH_SYMBOL_T )
809 {
810 kiapi::schematic::types::SchematicSymbolInstance symbol;
811
812 if( PackSymbol( &symbol, static_cast<SCH_SYMBOL*>( createdItem ), targetPath ) )
813 newItem.PackFrom( symbol );
814 }
815 else if( createdItem->Type() == SCH_SHEET_T )
816 {
817 kiapi::schematic::types::SheetSymbol sheet;
818
819 if( PackSheet( &sheet, static_cast<SCH_SHEET*>( createdItem ), targetPath ) )
820 newItem.PackFrom( sheet );
821 }
822 else
823 {
824 createdItem->Serialize( newItem );
825 }
826 }
827 else
828 {
829 commit->Modify( existingItem, targetScreen );
830 existingItem->SwapItemData( static_cast<SCH_ITEM*>( item.get() ) );
831
832 if( existingItem->Type() == SCH_SYMBOL_T )
833 {
834 SCH_SHEET_PATH path = existingPath;
835 kiapi::schematic::types::SchematicSymbolInstance symbol;
836
837 if( PackSymbol( &symbol, static_cast<SCH_SYMBOL*>( existingItem ), path ) )
838 newItem.PackFrom( symbol );
839 }
840 else if( existingItem->Type() == SCH_SHEET_T )
841 {
842 SCH_SHEET_PATH path = existingPath;
843 kiapi::schematic::types::SheetSymbol sheet;
844
845 if( PackSheet( &sheet, static_cast<SCH_SHEET*>( existingItem ), path ) )
846 newItem.PackFrom( sheet );
847 }
848 else
849 {
850 existingItem->Serialize( newItem );
851 }
852 }
853
854 aItemHandler( status, newItem );
855 }
856
857 if( !m_activeClients.contains( aClientName ) )
858 {
859 pushCurrentCommit( aClientName, aCreate ? _( "Created items via API" )
860 : _( "Modified items via API" ) );
861 }
862
863
864 return ItemRequestStatus::IRS_OK;
865}
866
867
868void API_HANDLER_SCH::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
869 const std::string& aClientName )
870{
871 SCH_SHEET_LIST hierarchy = schematic()->Hierarchy();
872 COMMIT* commit = getCurrentCommit( aClientName );
873
874 for( auto& [id, status] : aItemsToDelete )
875 {
877 SCH_ITEM* item = hierarchy.ResolveItem( id, &path, true );
878
879 if( !item )
880 continue;
881
882 if( !s_allowedTypes.contains( item->Type() ) )
883 {
884 status = ItemDeletionStatus::IDS_IMMUTABLE;
885 continue;
886 }
887
888 commit->Remove( item, path.LastScreen() );
889 status = ItemDeletionStatus::IDS_OK;
890 }
891
892 if( !m_activeClients.contains( aClientName ) )
893 pushCurrentCommit( aClientName, _( "Deleted items via API" ) );
894}
895
896
897std::optional<EDA_ITEM*> API_HANDLER_SCH::getItemFromDocument( const DocumentSpecifier& aDocument, const KIID& aId )
898{
899 if( !validateDocument( aDocument ) )
900 return std::nullopt;
901
902 SCH_ITEM* item = schematic()->Hierarchy().ResolveItem( aId, nullptr, true );
903
904 if( !item)
905 return std::nullopt;
906
907 return item;
908}
909
910
911std::optional<TITLE_BLOCK*> API_HANDLER_SCH::getTitleBlock()
912{
913 wxCHECK( m_context->GetCurrentSheet(), std::nullopt );
914 return &m_context->GetCurrentSheet()->LastScreen()->GetTitleBlock();
915}
916
917
918std::optional<PAGE_INFO> API_HANDLER_SCH::getPageSettings()
919{
920 wxCHECK( m_context->GetCurrentSheet(), std::nullopt );
921 return m_context->GetCurrentSheet()->LastScreen()->GetPageSettings();
922}
923
924
926{
927 wxCHECK( m_context->GetCurrentSheet(), false );
928 m_context->GetCurrentSheet()->LastScreen()->SetPageSettings( aPageInfo );
929 return true;
930}
931
932
937
938
939void API_HANDLER_SCH::setDrawingSheetFileName( const wxString& aFileName )
940{
943
944 if( m_frame )
945 m_frame->LoadDrawingSheet();
946}
947
948
950{
951 if( m_frame )
952 {
953 m_frame->Refresh();
954 m_frame->OnModify();
955 }
956}
957
958
961{
962 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
963 return tl::unexpected( *busy );
964
965 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
966
967 if( !documentValidation )
968 return tl::unexpected( documentValidation.error() );
969
970 auto plotJob = std::make_unique<JOB_EXPORT_SCH_PLOT_SVG>();
971 plotJob->m_filename = m_context->GetCurrentFileName();
972
973 if( !aCtx.Request.job_settings().output_path().empty() )
974 plotJob->SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
975
976 const kiapi::schematic::jobs::SchematicPlotSettings& settings = aCtx.Request.plot_settings();
977
978 plotJob->m_drawingSheet = wxString::FromUTF8( settings.drawing_sheet() );
979 plotJob->m_defaultFont = wxString::FromUTF8( settings.default_font() );
980 plotJob->m_variant = wxString::FromUTF8( settings.variant() );
981 plotJob->m_plotAll = settings.plot_all();
982 plotJob->m_plotDrawingSheet = settings.plot_drawing_sheet();
983 plotJob->m_show_hop_over = settings.show_hop_over();
984 plotJob->m_blackAndWhite = settings.black_and_white();
985 plotJob->m_useBackgroundColor = settings.use_background_color();
986 plotJob->m_minPenWidth = settings.min_pen_width();
987 plotJob->m_theme = wxString::FromUTF8( settings.theme() );
988
989 plotJob->m_plotPages.clear();
990
991 for( const std::string& page : settings.plot_pages() )
992 plotJob->m_plotPages.push_back( wxString::FromUTF8( page ) );
993
994 if( aCtx.Request.plot_settings().page_size() != kiapi::schematic::jobs::SchematicJobPageSize::SJPS_UNKNOWN )
995 {
996 plotJob->m_pageSizeSelect = FromProtoEnum<JOB_PAGE_SIZE>( aCtx.Request.plot_settings().page_size() );
997 }
998
999 return ExecuteSchematicJob( m_context->GetKiway(), *plotJob );
1000}
1001
1002
1005{
1006 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1007 return tl::unexpected( *busy );
1008
1009 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1010
1011 if( !documentValidation )
1012 return tl::unexpected( documentValidation.error() );
1013
1014 auto plotJob = std::make_unique<JOB_EXPORT_SCH_PLOT_DXF>();
1015 plotJob->m_filename = m_context->GetCurrentFileName();
1016
1017 if( !aCtx.Request.job_settings().output_path().empty() )
1018 plotJob->SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1019
1020 const kiapi::schematic::jobs::SchematicPlotSettings& settings = aCtx.Request.plot_settings();
1021
1022 plotJob->m_drawingSheet = wxString::FromUTF8( settings.drawing_sheet() );
1023 plotJob->m_defaultFont = wxString::FromUTF8( settings.default_font() );
1024 plotJob->m_variant = wxString::FromUTF8( settings.variant() );
1025 plotJob->m_plotAll = settings.plot_all();
1026 plotJob->m_plotDrawingSheet = settings.plot_drawing_sheet();
1027 plotJob->m_show_hop_over = settings.show_hop_over();
1028 plotJob->m_blackAndWhite = settings.black_and_white();
1029 plotJob->m_useBackgroundColor = settings.use_background_color();
1030 plotJob->m_minPenWidth = settings.min_pen_width();
1031 plotJob->m_theme = wxString::FromUTF8( settings.theme() );
1032
1033 plotJob->m_plotPages.clear();
1034
1035 for( const std::string& page : settings.plot_pages() )
1036 plotJob->m_plotPages.push_back( wxString::FromUTF8( page ) );
1037
1038 if( aCtx.Request.plot_settings().page_size() != kiapi::schematic::jobs::SchematicJobPageSize::SJPS_UNKNOWN )
1039 {
1040 plotJob->m_pageSizeSelect = FromProtoEnum<JOB_PAGE_SIZE>( aCtx.Request.plot_settings().page_size() );
1041 }
1042
1043 return ExecuteSchematicJob( m_context->GetKiway(), *plotJob );
1044}
1045
1046
1049{
1050 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1051 return tl::unexpected( *busy );
1052
1053 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1054
1055 if( !documentValidation )
1056 return tl::unexpected( documentValidation.error() );
1057
1058 auto plotJob = std::make_unique<JOB_EXPORT_SCH_PLOT_PDF>( false );
1059 plotJob->m_filename = m_context->GetCurrentFileName();
1060
1061 if( !aCtx.Request.job_settings().output_path().empty() )
1062 plotJob->SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1063
1064 const kiapi::schematic::jobs::SchematicPlotSettings& settings = aCtx.Request.plot_settings();
1065
1066 plotJob->m_drawingSheet = wxString::FromUTF8( settings.drawing_sheet() );
1067 plotJob->m_defaultFont = wxString::FromUTF8( settings.default_font() );
1068 plotJob->m_variant = wxString::FromUTF8( settings.variant() );
1069 plotJob->m_plotAll = settings.plot_all();
1070 plotJob->m_plotDrawingSheet = settings.plot_drawing_sheet();
1071 plotJob->m_show_hop_over = settings.show_hop_over();
1072 plotJob->m_blackAndWhite = settings.black_and_white();
1073 plotJob->m_useBackgroundColor = settings.use_background_color();
1074 plotJob->m_minPenWidth = settings.min_pen_width();
1075 plotJob->m_theme = wxString::FromUTF8( settings.theme() );
1076
1077 plotJob->m_plotPages.clear();
1078
1079 for( const std::string& page : settings.plot_pages() )
1080 plotJob->m_plotPages.push_back( wxString::FromUTF8( page ) );
1081
1082 if( aCtx.Request.plot_settings().page_size() != kiapi::schematic::jobs::SchematicJobPageSize::SJPS_UNKNOWN )
1083 {
1084 plotJob->m_pageSizeSelect = FromProtoEnum<JOB_PAGE_SIZE>( aCtx.Request.plot_settings().page_size() );
1085 }
1086
1087 plotJob->m_PDFPropertyPopups = aCtx.Request.property_popups();
1088 plotJob->m_PDFHierarchicalLinks = aCtx.Request.hierarchical_links();
1089 plotJob->m_PDFMetadata = aCtx.Request.include_metadata();
1090
1091 return ExecuteSchematicJob( m_context->GetKiway(), *plotJob );
1092}
1093
1094
1097{
1098 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1099 return tl::unexpected( *busy );
1100
1101 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1102
1103 if( !documentValidation )
1104 return tl::unexpected( documentValidation.error() );
1105
1106 auto plotJob = std::make_unique<JOB_EXPORT_SCH_PLOT_PS>();
1107 plotJob->m_filename = m_context->GetCurrentFileName();
1108
1109 if( !aCtx.Request.job_settings().output_path().empty() )
1110 plotJob->SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1111
1112 const kiapi::schematic::jobs::SchematicPlotSettings& settings = aCtx.Request.plot_settings();
1113
1114 plotJob->m_drawingSheet = wxString::FromUTF8( settings.drawing_sheet() );
1115 plotJob->m_defaultFont = wxString::FromUTF8( settings.default_font() );
1116 plotJob->m_variant = wxString::FromUTF8( settings.variant() );
1117 plotJob->m_plotAll = settings.plot_all();
1118 plotJob->m_plotDrawingSheet = settings.plot_drawing_sheet();
1119 plotJob->m_show_hop_over = settings.show_hop_over();
1120 plotJob->m_blackAndWhite = settings.black_and_white();
1121 plotJob->m_useBackgroundColor = settings.use_background_color();
1122 plotJob->m_minPenWidth = settings.min_pen_width();
1123 plotJob->m_theme = wxString::FromUTF8( settings.theme() );
1124
1125 plotJob->m_plotPages.clear();
1126
1127 for( const std::string& page : settings.plot_pages() )
1128 plotJob->m_plotPages.push_back( wxString::FromUTF8( page ) );
1129
1130 if( aCtx.Request.plot_settings().page_size() != kiapi::schematic::jobs::SchematicJobPageSize::SJPS_UNKNOWN )
1131 {
1132 plotJob->m_pageSizeSelect = FromProtoEnum<JOB_PAGE_SIZE>( aCtx.Request.plot_settings().page_size() );
1133 }
1134
1135 return ExecuteSchematicJob( m_context->GetKiway(), *plotJob );
1136}
1137
1138
1141{
1142 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1143 return tl::unexpected( *busy );
1144
1145 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1146
1147 if( !documentValidation )
1148 return tl::unexpected( documentValidation.error() );
1149
1150 if( aCtx.Request.format() == kiapi::schematic::jobs::SchematicNetlistFormat::SNF_UNKNOWN )
1151 {
1152 ApiResponseStatus e;
1153 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1154 e.set_error_message( "RunSchematicJobExportNetlist requires a valid format" );
1155 return tl::unexpected( e );
1156 }
1157
1158 JOB_EXPORT_SCH_NETLIST netlistJob;
1159 netlistJob.m_filename = m_context->GetCurrentFileName();
1160
1161 if( !aCtx.Request.job_settings().output_path().empty() )
1162 netlistJob.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1163
1164 netlistJob.format = FromProtoEnum<JOB_EXPORT_SCH_NETLIST::FORMAT>( aCtx.Request.format() );
1165
1166 if( !aCtx.Request.variant_name().empty() )
1167 netlistJob.m_variantNames.emplace_back( wxString::FromUTF8( aCtx.Request.variant_name() ) );
1168
1169 return ExecuteSchematicJob( m_context->GetKiway(), netlistJob );
1170}
1171
1172
1175{
1176 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1177 return tl::unexpected( *busy );
1178
1179 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.job_settings().document() );
1180
1181 if( !documentValidation )
1182 return tl::unexpected( documentValidation.error() );
1183
1184 JOB_EXPORT_SCH_BOM bomJob;
1185 bomJob.m_filename = m_context->GetCurrentFileName();
1186
1187 if( !aCtx.Request.job_settings().output_path().empty() )
1188 bomJob.SetConfiguredOutputPath( wxString::FromUTF8( aCtx.Request.job_settings().output_path() ) );
1189
1190 bomJob.m_bomFmtPresetName = wxString::FromUTF8( aCtx.Request.format().preset_name() );
1191 bomJob.m_fieldDelimiter = wxString::FromUTF8( aCtx.Request.format().field_delimiter() );
1192 bomJob.m_stringDelimiter = wxString::FromUTF8( aCtx.Request.format().string_delimiter() );
1193 bomJob.m_refDelimiter = wxString::FromUTF8( aCtx.Request.format().ref_delimiter() );
1194 bomJob.m_refRangeDelimiter = wxString::FromUTF8( aCtx.Request.format().ref_range_delimiter() );
1195 bomJob.m_keepTabs = aCtx.Request.format().keep_tabs();
1196 bomJob.m_keepLineBreaks = aCtx.Request.format().keep_line_breaks();
1197
1198 bomJob.m_bomPresetName = wxString::FromUTF8( aCtx.Request.fields().preset_name() );
1199 bomJob.m_sortField = wxString::FromUTF8( aCtx.Request.fields().sort_field() );
1200 bomJob.m_filterString = wxString::FromUTF8( aCtx.Request.fields().filter() );
1201
1202 if( aCtx.Request.fields().sort_direction() == kiapi::schematic::jobs::BOMSortDirection::BSD_ASCENDING )
1203 {
1204 bomJob.m_sortAsc = true;
1205 }
1206 else if( aCtx.Request.fields().sort_direction() == kiapi::schematic::jobs::BOMSortDirection::BSD_DESCENDING )
1207 {
1208 bomJob.m_sortAsc = false;
1209 }
1210
1211 for( const kiapi::schematic::jobs::BOMField& field : aCtx.Request.fields().fields() )
1212 {
1213 bomJob.m_fieldsOrdered.emplace_back( wxString::FromUTF8( field.name() ) );
1214 bomJob.m_fieldsLabels.emplace_back( wxString::FromUTF8( field.label() ) );
1215
1216 if( field.group_by() )
1217 bomJob.m_fieldsGroupBy.emplace_back( wxString::FromUTF8( field.name() ) );
1218 }
1219
1220 bomJob.m_excludeDNP = aCtx.Request.exclude_dnp();
1221 bomJob.m_groupSymbols = aCtx.Request.group_symbols();
1222
1223 if( !aCtx.Request.variant_name().empty() )
1224 bomJob.m_variantNames.emplace_back( wxString::FromUTF8( aCtx.Request.variant_name() ) );
1225
1226 return ExecuteSchematicJob( m_context->GetKiway(), bomJob );
1227}
1228
1229
1230void API_HANDLER_SCH::packSheetInstance( kiapi::schematic::types::SheetInstance* aInstance, SCH_SHEET_PATH& aPath,
1231 SCH_SHEET* aSheet )
1232{
1233 aPath.push_back( aSheet );
1234
1235 PackSheetPath( *aInstance->mutable_path(), aPath.Path() );
1236
1237 wxString sheetName = aSheet->GetShownName( false );
1238
1239 if( sheetName.IsEmpty() && aSheet->GetScreen() )
1240 {
1241 wxFileName fn( aSheet->GetScreen()->GetFileName() );
1242 sheetName = fn.GetName();
1243 }
1244
1245 aInstance->set_name( sheetName.ToUTF8() );
1246 aInstance->set_filename( aSheet->GetFileName().ToUTF8() );
1247 aInstance->set_page_number( aPath.GetPageNumber().ToUTF8() );
1248
1249 if( aSheet->GetScreen() )
1250 {
1251 std::vector<SCH_ITEM*> childSheets;
1252 aSheet->GetScreen()->GetSheets( &childSheets );
1253
1254 std::ranges::sort( childSheets,
1255 [&]( SCH_ITEM* a, SCH_ITEM* b )
1256 {
1257 SCH_SHEET_PATH pathA = aPath;
1258 pathA.push_back( static_cast<SCH_SHEET*>( a ) );
1259
1260 SCH_SHEET_PATH pathB = aPath;
1261 pathB.push_back( static_cast<SCH_SHEET*>( b ) );
1262
1263 return pathA.ComparePageNum( pathB ) < 0;
1264 } );
1265
1266 for( SCH_ITEM* childItem : childSheets )
1267 {
1268 SCH_SHEET* childSheet = static_cast<SCH_SHEET*>( childItem );
1269 kiapi::schematic::types::SheetInstance* childInstance = aInstance->add_children();
1270 packSheetInstance( childInstance, aPath, childSheet );
1271 }
1272 }
1273
1274 aPath.pop_back();
1275}
1276
1277
1280{
1281 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
1282
1283 if( !documentValidation )
1284 return tl::unexpected( documentValidation.error() );
1285
1286 kiapi::schematic::types::SchematicHierarchyResponse response;
1287 response.mutable_document()->CopyFrom( aCtx.Request.document() );
1288
1289 if( !schematic()->HasHierarchy() )
1291
1293 std::vector<SCH_SHEET*> topLevelSheets = schematic()->GetTopLevelSheets();
1294
1295 std::ranges::sort( topLevelSheets,
1296 [&]( SCH_SHEET* a, SCH_SHEET* b )
1297 {
1298 SCH_SHEET_PATH pathA;
1299 pathA.push_back( a );
1300
1301 SCH_SHEET_PATH pathB;
1302 pathB.push_back( b );
1303
1304 return pathA.ComparePageNum( pathB ) < 0;
1305 } );
1306
1307 for( SCH_SHEET* topSheet : topLevelSheets )
1308 {
1309 kiapi::schematic::types::SheetInstance* instance = response.add_top_level_sheets();
1310 packSheetInstance( instance, path, topSheet );
1311 }
1312
1313 return response;
1314}
1315
1316
1319{
1320 if( std::optional<ApiResponseStatus> busy = checkForBusy() )
1321 return tl::unexpected( *busy );
1322
1323 HANDLER_RESULT<bool> documentValidation = validateDocument( aCtx.Request.document() );
1324
1325 if( !documentValidation )
1326 return tl::unexpected( documentValidation.error() );
1327
1328 std::vector<KICAD_T> types = parseRequestedItemTypes( aCtx.Request.types() );
1329 const bool filterByType = aCtx.Request.types_size() > 0;
1330
1331 if( filterByType && types.empty() )
1332 {
1333 ApiResponseStatus e;
1334 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1335 e.set_error_message( "none of the requested types are valid for a Schematic object" );
1336 return tl::unexpected( e );
1337 }
1338
1339 std::set<KICAD_T> typeFilter( types.begin(), types.end() );
1340
1341 CONNECTION_GRAPH* connectionGraph = schematic()->ConnectionGraph();
1342
1343 if( !connectionGraph )
1344 {
1345 ApiResponseStatus e;
1346 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
1347 e.set_error_message( "schematic has no connection graph" );
1348 return tl::unexpected( e );
1349 }
1350
1351 kiapi::schematic::types::SchematicNetlistResponse response;
1352 response.mutable_document()->CopyFrom( aCtx.Request.document() );
1353
1354 for( const auto& [key, subgraphList] : connectionGraph->GetNetMap() )
1355 {
1356 if( subgraphList.empty() )
1357 continue;
1358
1359 CONNECTION_SUBGRAPH* firstSubgraph = subgraphList[0];
1360
1361 if( firstSubgraph->GetDriverConnection() && firstSubgraph->GetDriverConnection()->IsBus() )
1362 continue;
1363
1365 continue;
1366
1367 kiapi::schematic::types::SchematicNet* net = response.add_nets();
1368 net->set_name( key.Name.ToUTF8() );
1369
1370 for( CONNECTION_SUBGRAPH* subGraph : subgraphList )
1371 {
1372 kiapi::schematic::types::SchematicNetSheetContents* sheetContents = net->add_sheets();
1373 PackSheetPath( *sheetContents->mutable_path(), subGraph->GetSheet().Path() );
1374
1375 for( SCH_ITEM* item : subGraph->GetItems() )
1376 {
1377 if( filterByType && !typeFilter.contains( item->Type() ) )
1378 continue;
1379
1380 sheetContents->add_items()->set_value( item->m_Uuid.AsStdString() );
1381 }
1382 }
1383 }
1384
1385 return response;
1386}
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:47
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition api_handler.h:45
HANDLER_RESULT< types::RunJobResponse > ExecuteSchematicJob(KIWAY *aKiway, JOB &aJob)
std::unique_ptr< EDA_ITEM > CreateItemForType(KICAD_T aType, EDA_ITEM *aContainer)
tl::expected< bool, ApiResponseStatus > UnpackSheet(SCH_SHEET *aOutput, const kiapi::schematic::types::SheetSymbol &aInput)
bool PackSheet(kiapi::schematic::types::SheetSymbol *aOutput, const SCH_SHEET *aInput, const SCH_SHEET_PATH &aPath)
bool PackSymbol(kiapi::schematic::types::SchematicSymbolInstance *aOutput, const SCH_SYMBOL *aInput, const SCH_SHEET_PATH &aPath)
bool UnpackSymbol(SCH_SYMBOL *aOutput, const kiapi::schematic::types::SchematicSymbolInstance &aInput)
BASE_SCREEN class implementation.
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)
API_HANDLER_EDITOR(EDA_BASE_FRAME *aFrame=nullptr)
static std::vector< KICAD_T > parseRequestedItemTypes(const google::protobuf::RepeatedField< int > &aTypes)
COMMIT * getCurrentCommit(const std::string &aClientName)
virtual void pushCurrentCommit(const std::string &aClientName, const wxString &aMessage)
std::set< std::string > m_activeClients
virtual std::optional< ApiResponseStatus > checkForBusy()
Checks if the editor can accept commands.
HANDLER_RESULT< types::RunJobResponse > handleRunSchematicJobExportDxf(const HANDLER_CONTEXT< kiapi::schematic::jobs::RunSchematicJobExportDxf > &aCtx)
std::optional< TITLE_BLOCK * > getTitleBlock() override
wxString getDrawingSheetFileName() override
SCH_EDIT_FRAME * m_frame
HANDLER_RESULT< kiapi::schematic::types::SchematicHierarchyResponse > handleGetSchematicHierarchy(const HANDLER_CONTEXT< kiapi::schematic::types::GetSchematicHierarchy > &aCtx)
std::unique_ptr< COMMIT > createCommit() override
Override this to create an appropriate COMMIT subclass for the frame in question.
SCHEMATIC * schematic() const
HANDLER_RESULT< kiapi::schematic::types::SchematicNetlistResponse > handleGetSchematicNetlist(const HANDLER_CONTEXT< kiapi::schematic::types::GetSchematicNetlist > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunSchematicJobExportPdf(const HANDLER_CONTEXT< kiapi::schematic::jobs::RunSchematicJobExportPdf > &aCtx)
static std::set< KICAD_T > s_allowedTypes
HANDLER_RESULT< commands::GetItemsResponse > handleGetItemsById(const HANDLER_CONTEXT< commands::GetItemsById > &aCtx)
HANDLER_RESULT< google::protobuf::Empty > handleSaveDocument(const HANDLER_CONTEXT< commands::SaveDocument > &aCtx)
std::optional< SCH_ITEM * > getItemById(const KIID &aId, SCH_SHEET_PATH *aPathOut=nullptr) const
bool setPageSettings(const PAGE_INFO &aPageInfo) override
std::shared_ptr< SCH_CONTEXT > m_context
std::optional< EDA_ITEM * > getItemFromDocument(const DocumentSpecifier &aDocument, const KIID &aId) override
TOOL_MANAGER * toolManager() const
HANDLER_RESULT< types::RunJobResponse > handleRunSchematicJobExportSvg(const HANDLER_CONTEXT< kiapi::schematic::jobs::RunSchematicJobExportSvg > &aCtx)
HANDLER_RESULT< std::unique_ptr< EDA_ITEM > > createItemForType(KICAD_T aType, EDA_ITEM *aContainer)
void filterValidSchTypes(std::set< KICAD_T > &aTypeList)
HANDLER_RESULT< types::RunJobResponse > handleRunSchematicJobExportBOM(const HANDLER_CONTEXT< kiapi::schematic::jobs::RunSchematicJobExportBOM > &aCtx)
std::optional< PAGE_INFO > getPageSettings() override
HANDLER_RESULT< types::RunJobResponse > handleRunSchematicJobExportPs(const HANDLER_CONTEXT< kiapi::schematic::jobs::RunSchematicJobExportPs > &aCtx)
PROJECT & project() const
void onModified() override
API_HANDLER_SCH(SCH_EDIT_FRAME *aFrame)
HANDLER_RESULT< types::ItemRequestStatus > handleCreateUpdateItemsInternal(bool aCreate, const std::string &aClientName, const types::ItemHeader &aHeader, const google::protobuf::RepeatedPtrField< google::protobuf::Any > &aItems, std::function< void(commands::ItemStatus, google::protobuf::Any)> aItemHandler) override
HANDLER_RESULT< commands::GetOpenDocumentsResponse > handleGetOpenDocuments(const HANDLER_CONTEXT< commands::GetOpenDocuments > &aCtx)
HANDLER_RESULT< types::RunJobResponse > handleRunSchematicJobExportNetlist(const HANDLER_CONTEXT< kiapi::schematic::jobs::RunSchematicJobExportNetlist > &aCtx)
void deleteItemsInternal(std::map< KIID, ItemDeletionStatus > &aItemsToDelete, const std::string &aClientName) override
void packSheetInstance(kiapi::schematic::types::SheetInstance *aInstance, SCH_SHEET_PATH &aPath, SCH_SHEET *aSheet)
tl::expected< bool, ApiResponseStatus > validateDocumentInternal(const DocumentSpecifier &aDocument) const override
HANDLER_RESULT< google::protobuf::Empty > handleSaveCopyOfDocument(const HANDLER_CONTEXT< commands::SaveCopyOfDocument > &aCtx)
SCH_CONTEXT * context() const
void setDrawingSheetFileName(const wxString &aFileName) override
HANDLER_RESULT< commands::GetItemsResponse > handleGetItems(const HANDLER_CONTEXT< commands::GetItems > &aCtx)
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
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:68
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
Calculate the connectivity of a schematic and generates netlists.
const NET_MAP & GetNetMap() const
A subgraph is a set of items that are electrically connected on a single sheet.
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Return the priority (higher is more important) of a candidate driver.
const SCH_CONNECTION * GetDriverConnection() const
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual wxString GetFriendlyName() const
Definition eda_item.cpp:426
std::vector< wxString > m_fieldsLabels
std::vector< wxString > m_fieldsOrdered
std::vector< wxString > m_fieldsGroupBy
std::vector< wxString > m_variantNames
std::vector< wxString > m_variantNames
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
Definition kiid.h:44
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:311
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:746
@ FACE_SCH
eeschema DSO
Definition kiway.h:318
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
Container for project specific data.
Definition project.h:62
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:183
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition project.cpp:195
Holds all the data relating to one schematic.
Definition schematic.h:90
SCHEMATIC_SETTINGS & Settings() const
SCH_SCREEN * GetCurrentScreen() const
Definition schematic.h:199
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Definition schematic.h:128
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:201
std::vector< SCH_SHEET * > GetTopLevelSheets() const
Get the list of top-level sheets.
void RefreshHierarchy()
bool IsBus() const
Schematic editor (Eeschema) main window.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
void SwapItemData(SCH_ITEM *aImage)
Swap data between aItem and aImage.
Definition sch_item.cpp:632
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
const wxString & GetFileName() const
Definition sch_screen.h:150
void GetSheets(std::vector< SCH_ITEM * > *aItems) const
Similar to Items().OfType( SCH_SHEET_T ), but return the sheets in a deterministic order (L-R,...
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
std::optional< SCH_SHEET_PATH > GetSheetPathByKIIDPath(const KIID_PATH &aPath, bool aIncludeLastSheet=true) const
Finds a SCH_SHEET_PATH that matches the provided KIID_PATH.
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Fetch a SCH_ITEM by ID.
wxString GetNextPageNumber() const
bool TestForRecursion(const SCH_SHEET_LIST &aSrcSheetHierarchy, const wxString &aDestFileName)
Test every SCH_SHEET_PATH in this SCH_SHEET_LIST to verify if adding the sheets stored in aSrcSheetHi...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
int ComparePageNum(const SCH_SHEET_PATH &aSheetPathToTest) const
Compare sheets by their page number.
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_ITEM * ResolveItem(const KIID &aID) const
Fetch a SCH_ITEM by ID.
SCH_SCREEN * LastScreen()
wxString GetPageNumber() const
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
void pop_back()
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:370
void AddInstance(const SCH_SHEET_INSTANCE &aInstance)
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
wxString GetShownName(bool aAllowExtraText) const
Definition sch_sheet.h:132
const std::vector< SCH_SHEET_INSTANCE > & GetInstances() const
Definition sch_sheet.h:505
Schematic symbol object.
Definition sch_symbol.h:69
virtual void Serialize(google::protobuf::Any &aContainer) const
Serializes this object to the given Any message.
A wrapper for reporting to a wxString object.
Definition reporter.h:189
A type-safe container of any type.
Definition ki_any.h:92
#define _(s)
@ NO_RECURSE
Definition eda_item.h:50
static const std::string KiCadSchematicFileExtension
KICOMMON_API void PackProject(types::ProjectSpecifier &aOutput, const PROJECT &aInput)
KICOMMON_API KIID_PATH UnpackSheetPath(const types::SheetPath &aInput)
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition api_utils.cpp:35
KICOMMON_API void PackSheetPath(types::SheetPath &aOutput, const KIID_PATH &aInput)
STL namespace.
std::shared_ptr< SCH_CONTEXT > CreateSchFrameContext(SCH_EDIT_FRAME *aFrame)
Class to handle a set of SCH_ITEMs.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
RequestMessageType Request
Definition api_handler.h:52
A simple container for sheet instance information.
std::string path
IbisParser parser & reporter
nlohmann::json output
wxString result
Test unit parsing edge cases and error handling.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:71
@ SCH_GROUP_T
Definition typeinfo.h:170
@ SCH_LINE_T
Definition typeinfo.h:160
@ SCH_NO_CONNECT_T
Definition typeinfo.h:157
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:164
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_SHAPE_T
Definition typeinfo.h:146
@ SCH_HIER_LABEL_T
Definition typeinfo.h:166
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:159
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:158
@ SCH_BITMAP_T
Definition typeinfo.h:161
@ SCH_TEXTBOX_T
Definition typeinfo.h:149
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:165
@ SCH_JUNCTION_T
Definition typeinfo.h:156
@ SCH_PIN_T
Definition typeinfo.h:150
Definition of file extensions used in Kicad.