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 (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <api/api_handler_sch.h>
22#include <api/api_sch_utils.h>
23#include <api/api_utils.h>
24#include <magic_enum.hpp>
25#include <sch_commit.h>
26#include <sch_edit_frame.h>
27#include <wx/filename.h>
28
29#include <api/common/types/base_types.pb.h>
30
31using namespace kiapi::common::commands;
32using kiapi::common::types::CommandStatus;
33using kiapi::common::types::DocumentType;
34using kiapi::common::types::ItemRequestStatus;
35
36
39 m_frame( aFrame )
40{
41 registerHandler<GetOpenDocuments, GetOpenDocumentsResponse>(
43}
44
45
46std::unique_ptr<COMMIT> API_HANDLER_SCH::createCommit()
47{
48 return std::make_unique<SCH_COMMIT>( m_frame );
49}
50
51
52bool API_HANDLER_SCH::validateDocumentInternal( const DocumentSpecifier& aDocument ) const
53{
54 if( aDocument.type() != DocumentType::DOCTYPE_SCHEMATIC )
55 return false;
56
57 // TODO(JE) need serdes for SCH_SHEET_PATH <> SheetPath
58 return true;
59 //wxString currentPath = m_frame->GetCurrentSheet().PathAsString();
60 //return 0 == aDocument.sheet_path().compare( currentPath.ToStdString() );
61}
62
63
66{
67 if( aCtx.Request.type() != DocumentType::DOCTYPE_SCHEMATIC )
68 {
69 ApiResponseStatus e;
70 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
71 e.set_status( ApiStatusCode::AS_UNHANDLED );
72 return tl::unexpected( e );
73 }
74
75 GetOpenDocumentsResponse response;
76 common::types::DocumentSpecifier doc;
77
78 wxFileName fn( m_frame->GetCurrentFileName() );
79
80 doc.set_type( DocumentType::DOCTYPE_SCHEMATIC );
81 doc.set_board_filename( fn.GetFullName() );
82
83 response.mutable_documents()->Add( std::move( doc ) );
84 return response;
85}
86
87
89 EDA_ITEM* aContainer )
90{
91 if( !aContainer )
92 {
93 ApiResponseStatus e;
94 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
95 e.set_error_message( "Tried to create an item in a null container" );
96 return tl::unexpected( e );
97 }
98
99 if( aType == SCH_PIN_T && !dynamic_cast<SCH_SYMBOL*>( aContainer ) )
100 {
101 ApiResponseStatus e;
102 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
103 e.set_error_message( fmt::format( "Tried to create a pin in {}, which is not a symbol",
104 aContainer->GetFriendlyName().ToStdString() ) );
105 return tl::unexpected( e );
106 }
107 else if( aType == SCH_SYMBOL_T && !dynamic_cast<SCHEMATIC*>( aContainer ) )
108 {
109 ApiResponseStatus e;
110 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
111 e.set_error_message( fmt::format( "Tried to create a symbol in {}, which is not a schematic",
112 aContainer->GetFriendlyName().ToStdString() ) );
113 return tl::unexpected( e );
114 }
115
116 std::unique_ptr<EDA_ITEM> created = CreateItemForType( aType, aContainer );
117
118 if( !created )
119 {
120 ApiResponseStatus e;
121 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
122 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
123 magic_enum::enum_name( aType ) ) );
124 return tl::unexpected( e );
125 }
126
127 return created;
128}
129
130
132 const std::string& aClientName,
133 const types::ItemHeader &aHeader,
134 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
135 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
136{
137 ApiResponseStatus e;
138
139 auto containerResult = validateItemHeaderDocument( aHeader );
140
141 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
142 {
143 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
144 e.set_status( ApiStatusCode::AS_UNHANDLED );
145 return tl::unexpected( e );
146 }
147 else if( !containerResult )
148 {
149 e.CopyFrom( containerResult.error() );
150 return tl::unexpected( e );
151 }
152
153 SCH_SCREEN* screen = m_frame->GetScreen();
154 EE_RTREE& screenItems = screen->Items();
155
156 std::map<KIID, EDA_ITEM*> itemUuidMap;
157
158 std::for_each( screenItems.begin(), screenItems.end(),
159 [&]( EDA_ITEM* aItem )
160 {
161 itemUuidMap[aItem->m_Uuid] = aItem;
162 } );
163
164 EDA_ITEM* container = nullptr;
165
166 if( containerResult->has_value() )
167 {
168 const KIID& containerId = **containerResult;
169
170 if( itemUuidMap.count( containerId ) )
171 {
172 container = itemUuidMap.at( containerId );
173
174 if( !container )
175 {
176 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
177 e.set_error_message( fmt::format(
178 "The requested container {} is not a valid schematic item container",
179 containerId.AsStdString() ) );
180 return tl::unexpected( e );
181 }
182 }
183 else
184 {
185 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
186 e.set_error_message( fmt::format(
187 "The requested container {} does not exist in this document",
188 containerId.AsStdString() ) );
189 return tl::unexpected( e );
190 }
191 }
192
193 COMMIT* commit = getCurrentCommit( aClientName );
194
195 for( const google::protobuf::Any& anyItem : aItems )
196 {
197 ItemStatus status;
198 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
199
200 if( !type )
201 {
202 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
203 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
204 anyItem.type_url() ) );
205 aItemHandler( status, anyItem );
206 continue;
207 }
208
210 createItemForType( *type, container );
211
212 if( !creationResult )
213 {
214 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
215 status.set_error_message( creationResult.error().error_message() );
216 aItemHandler( status, anyItem );
217 continue;
218 }
219
220 std::unique_ptr<EDA_ITEM> item( std::move( *creationResult ) );
221
222 if( !item->Deserialize( anyItem ) )
223 {
224 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
225 e.set_error_message( fmt::format( "could not unpack {} from request",
226 item->GetClass().ToStdString() ) );
227 return tl::unexpected( e );
228 }
229
230 if( aCreate && itemUuidMap.count( item->m_Uuid ) )
231 {
232 status.set_code( ItemStatusCode::ISC_EXISTING );
233 status.set_error_message( fmt::format( "an item with UUID {} already exists",
234 item->m_Uuid.AsStdString() ) );
235 aItemHandler( status, anyItem );
236 continue;
237 }
238 else if( !aCreate && !itemUuidMap.count( item->m_Uuid ) )
239 {
240 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
241 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
242 item->m_Uuid.AsStdString() ) );
243 aItemHandler( status, anyItem );
244 continue;
245 }
246
247 status.set_code( ItemStatusCode::ISC_OK );
248 google::protobuf::Any newItem;
249
250 if( aCreate )
251 {
252 item->Serialize( newItem );
253 commit->Add( item.release() );
254
255 if( !m_activeClients.count( aClientName ) )
256 pushCurrentCommit( aClientName, _( "Added items via API" ) );
257 }
258 else
259 {
260 EDA_ITEM* edaItem = itemUuidMap[item->m_Uuid];
261
262 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( edaItem ) )
263 {
264 schItem->SwapData( static_cast<SCH_ITEM*>( item.get() ) );
265 schItem->Serialize( newItem );
266 commit->Modify( schItem );
267 }
268 else
269 {
270 wxASSERT( false );
271 }
272
273 if( !m_activeClients.count( aClientName ) )
274 pushCurrentCommit( aClientName, _( "Created items via API" ) );
275 }
276
277 aItemHandler( status, newItem );
278 }
279
280
281 return ItemRequestStatus::IRS_OK;
282}
283
284
285void API_HANDLER_SCH::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
286 const std::string& aClientName )
287{
288 // TODO
289}
290
291
292std::optional<EDA_ITEM*> API_HANDLER_SCH::getItemFromDocument( const DocumentSpecifier& aDocument,
293 const KIID& aId )
294{
295 if( !validateDocument( aDocument ) )
296 return std::nullopt;
297
298 // TODO
299
300 return std::nullopt;
301}
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition: api_handler.h:45
std::unique_ptr< EDA_ITEM > CreateItemForType(KICAD_T aType, EDA_ITEM *aContainer)
Base class for API handlers related to editor frames.
HANDLER_RESULT< bool > validateDocument(const DocumentSpecifier &aDocument)
HANDLER_RESULT< std::optional< KIID > > validateItemHeaderDocument(const kiapi::common::types::ItemHeader &aHeader)
If the header is valid, returns the item container.
COMMIT * getCurrentCommit(const std::string &aClientName)
virtual void pushCurrentCommit(const std::string &aClientName, const wxString &aMessage)
std::set< std::string > m_activeClients
SCH_EDIT_FRAME * m_frame
std::unique_ptr< COMMIT > createCommit() override
Override this to create an appropriate COMMIT subclass for the frame in question.
std::optional< EDA_ITEM * > getItemFromDocument(const DocumentSpecifier &aDocument, const KIID &aId) override
HANDLER_RESULT< std::unique_ptr< EDA_ITEM > > createItemForType(KICAD_T aType, EDA_ITEM *aContainer)
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)
bool validateDocumentInternal(const DocumentSpecifier &aDocument) const override
void deleteItemsInternal(std::map< KIID, ItemDeletionStatus > &aItemsToDelete, const std::string &aClientName) override
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition: commit.h:74
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:80
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual wxString GetFriendlyName() const
Definition: eda_item.cpp:332
Implements an R-tree for fast spatial and type indexing of schematic items.
Definition: sch_rtree.h:40
iterator end()
Returns a read/write iterator that points to one past the last element in the EE_RTREE.
Definition: sch_rtree.h:285
iterator begin()
Returns a read/write iterator that points to the first element in the EE_RTREE N.B.
Definition: sch_rtree.h:276
Definition: kiid.h:49
std::string AsStdString() const
Definition: kiid.cpp:244
Holds all the data relating to one schematic.
Definition: schematic.h:77
Schematic editor (Eeschema) main window.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
wxString GetCurrentFileName() const override
Get the full filename + path of the currently opened file in the frame.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:166
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:108
Schematic symbol object.
Definition: sch_symbol.h:104
virtual void Serialize(google::protobuf::Any &aContainer) const
Serializes this object to the given Any message.
#define _(s)
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition: api_utils.cpp:32
RequestMessageType Request
Definition: api_handler.h:52
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:78
@ SCH_SYMBOL_T
Definition: typeinfo.h:172
@ SCH_PIN_T
Definition: typeinfo.h:153