|
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2024 Jon Evans <jon@craftyjon.com> * Copyright The KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <api/api_handler_sch.h>
#include <api/api_sch_utils.h>
#include <api/api_utils.h>
#include <magic_enum.hpp>
#include <sch_commit.h>
#include <sch_edit_frame.h>
#include <wx/filename.h>
#include <api/common/types/base_types.pb.h>
using namespace kiapi::common::commands;using kiapi::common::types::CommandStatus;using kiapi::common::types::DocumentType;using kiapi::common::types::ItemRequestStatus;
API_HANDLER_SCH::API_HANDLER_SCH( SCH_EDIT_FRAME* aFrame ) : API_HANDLER_EDITOR(), m_frame( aFrame ){ registerHandler<GetOpenDocuments, GetOpenDocumentsResponse>( &API_HANDLER_SCH::handleGetOpenDocuments );}
std::unique_ptr<COMMIT> API_HANDLER_SCH::createCommit(){ return std::make_unique<SCH_COMMIT>( m_frame );}
bool API_HANDLER_SCH::validateDocumentInternal( const DocumentSpecifier& aDocument ) const{ if( aDocument.type() != DocumentType::DOCTYPE_SCHEMATIC ) return false;
// TODO(JE) need serdes for SCH_SHEET_PATH <> SheetPath
return true;
//wxString currentPath = m_frame->GetCurrentSheet().PathAsString();
//return 0 == aDocument.sheet_path().compare( currentPath.ToStdString() );
}
HANDLER_RESULT<GetOpenDocumentsResponse> API_HANDLER_SCH::handleGetOpenDocuments( const HANDLER_CONTEXT<GetOpenDocuments>& aCtx ){ if( aCtx.Request.type() != DocumentType::DOCTYPE_SCHEMATIC ) { ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED ); return tl::unexpected( e ); }
GetOpenDocumentsResponse response; common::types::DocumentSpecifier doc;
wxFileName fn( m_frame->GetCurrentFileName() );
doc.set_type( DocumentType::DOCTYPE_SCHEMATIC ); doc.set_board_filename( fn.GetFullName() );
response.mutable_documents()->Add( std::move( doc ) ); return response;}
HANDLER_RESULT<std::unique_ptr<EDA_ITEM>> API_HANDLER_SCH::createItemForType( KICAD_T aType, EDA_ITEM* aContainer ){ if( !aContainer ) { ApiResponseStatus e; e.set_status( ApiStatusCode::AS_BAD_REQUEST ); e.set_error_message( "Tried to create an item in a null container" ); return tl::unexpected( e ); }
if( aType == SCH_PIN_T && !dynamic_cast<SCH_SYMBOL*>( aContainer ) ) { ApiResponseStatus e; e.set_status( ApiStatusCode::AS_BAD_REQUEST ); e.set_error_message( fmt::format( "Tried to create a pin in {}, which is not a symbol", aContainer->GetFriendlyName().ToStdString() ) ); return tl::unexpected( e ); } else if( aType == SCH_SYMBOL_T && !dynamic_cast<SCHEMATIC*>( aContainer ) ) { ApiResponseStatus e; e.set_status( ApiStatusCode::AS_BAD_REQUEST ); e.set_error_message( fmt::format( "Tried to create a symbol in {}, which is not a " "schematic", aContainer->GetFriendlyName().ToStdString() ) ); return tl::unexpected( e ); }
std::unique_ptr<EDA_ITEM> created = CreateItemForType( aType, aContainer );
if( !created ) { ApiResponseStatus e; e.set_status( ApiStatusCode::AS_BAD_REQUEST ); e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled", magic_enum::enum_name( aType ) ) ); return tl::unexpected( e ); }
return created;}
HANDLER_RESULT<ItemRequestStatus> API_HANDLER_SCH::handleCreateUpdateItemsInternal( bool aCreate, const std::string& aClientName, const types::ItemHeader &aHeader, const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems, std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler ){ ApiResponseStatus e;
auto containerResult = validateItemHeaderDocument( aHeader );
if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED ) { // No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED ); return tl::unexpected( e ); } else if( !containerResult ) { e.CopyFrom( containerResult.error() ); return tl::unexpected( e ); }
SCH_SCREEN* screen = m_frame->GetScreen(); EE_RTREE& screenItems = screen->Items();
std::map<KIID, EDA_ITEM*> itemUuidMap;
std::for_each( screenItems.begin(), screenItems.end(), [&]( EDA_ITEM* aItem ) { itemUuidMap[aItem->m_Uuid] = aItem; } );
EDA_ITEM* container = nullptr;
if( containerResult->has_value() ) { const KIID& containerId = **containerResult;
if( itemUuidMap.count( containerId ) ) { container = itemUuidMap.at( containerId );
if( !container ) { e.set_status( ApiStatusCode::AS_BAD_REQUEST ); e.set_error_message( fmt::format( "The requested container {} is not a valid schematic item container", containerId.AsStdString() ) ); return tl::unexpected( e ); } } else { e.set_status( ApiStatusCode::AS_BAD_REQUEST ); e.set_error_message( fmt::format( "The requested container {} does not exist in this document", containerId.AsStdString() ) ); return tl::unexpected( e ); } }
COMMIT* commit = getCurrentCommit( aClientName );
for( const google::protobuf::Any& anyItem : aItems ) { ItemStatus status; std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
if( !type ) { status.set_code( ItemStatusCode::ISC_INVALID_TYPE ); status.set_error_message( fmt::format( "Could not decode a valid type from {}", anyItem.type_url() ) ); aItemHandler( status, anyItem ); continue; }
HANDLER_RESULT<std::unique_ptr<EDA_ITEM>> creationResult = createItemForType( *type, container );
if( !creationResult ) { status.set_code( ItemStatusCode::ISC_INVALID_TYPE ); status.set_error_message( creationResult.error().error_message() ); aItemHandler( status, anyItem ); continue; }
std::unique_ptr<EDA_ITEM> item( std::move( *creationResult ) );
if( !item->Deserialize( anyItem ) ) { e.set_status( ApiStatusCode::AS_BAD_REQUEST ); e.set_error_message( fmt::format( "could not unpack {} from request", item->GetClass().ToStdString() ) ); return tl::unexpected( e ); }
if( aCreate && itemUuidMap.count( item->m_Uuid ) ) { status.set_code( ItemStatusCode::ISC_EXISTING ); status.set_error_message( fmt::format( "an item with UUID {} already exists", item->m_Uuid.AsStdString() ) ); aItemHandler( status, anyItem ); continue; } else if( !aCreate && !itemUuidMap.count( item->m_Uuid ) ) { status.set_code( ItemStatusCode::ISC_NONEXISTENT ); status.set_error_message( fmt::format( "an item with UUID {} does not exist", item->m_Uuid.AsStdString() ) ); aItemHandler( status, anyItem ); continue; }
status.set_code( ItemStatusCode::ISC_OK ); google::protobuf::Any newItem;
if( aCreate ) { item->Serialize( newItem ); commit->Add( item.release() );
if( !m_activeClients.count( aClientName ) ) pushCurrentCommit( aClientName, _( "Added items via API" ) ); } else { EDA_ITEM* edaItem = itemUuidMap[item->m_Uuid];
if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( edaItem ) ) { schItem->SwapItemData( static_cast<SCH_ITEM*>( item.get() ) ); schItem->Serialize( newItem ); commit->Modify( schItem ); } else { wxASSERT( false ); }
if( !m_activeClients.count( aClientName ) ) pushCurrentCommit( aClientName, _( "Created items via API" ) ); }
aItemHandler( status, newItem ); }
return ItemRequestStatus::IRS_OK;}
void API_HANDLER_SCH::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete, const std::string& aClientName ){ // TODO
}
std::optional<EDA_ITEM*> API_HANDLER_SCH::getItemFromDocument( const DocumentSpecifier& aDocument, const KIID& aId ){ if( !validateDocument( aDocument ) ) return std::nullopt;
// TODO
return std::nullopt;}
|