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 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
60 //wxString currentPath = m_frame->GetCurrentSheet().PathAsString();
61 //return 0 == aDocument.sheet_path().compare( currentPath.ToStdString() );
62}
63
64
67{
68 if( aCtx.Request.type() != DocumentType::DOCTYPE_SCHEMATIC )
69 {
70 ApiResponseStatus e;
71
72 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
73 e.set_status( ApiStatusCode::AS_UNHANDLED );
74 return tl::unexpected( e );
75 }
76
77 GetOpenDocumentsResponse response;
78 common::types::DocumentSpecifier doc;
79
80 wxFileName fn( m_frame->GetCurrentFileName() );
81
82 doc.set_type( DocumentType::DOCTYPE_SCHEMATIC );
83 doc.set_board_filename( fn.GetFullName() );
84
85 response.mutable_documents()->Add( std::move( doc ) );
86 return response;
87}
88
89
91 EDA_ITEM* aContainer )
92{
93 if( !aContainer )
94 {
95 ApiResponseStatus e;
96 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
97 e.set_error_message( "Tried to create an item in a null container" );
98 return tl::unexpected( e );
99 }
100
101 if( aType == SCH_PIN_T && !dynamic_cast<SCH_SYMBOL*>( aContainer ) )
102 {
103 ApiResponseStatus e;
104 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
105 e.set_error_message( fmt::format( "Tried to create a pin in {}, which is not a symbol",
106 aContainer->GetFriendlyName().ToStdString() ) );
107 return tl::unexpected( e );
108 }
109 else if( aType == SCH_SYMBOL_T && !dynamic_cast<SCHEMATIC*>( aContainer ) )
110 {
111 ApiResponseStatus e;
112 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
113 e.set_error_message( fmt::format( "Tried to create a symbol in {}, which is not a "
114 "schematic",
115 aContainer->GetFriendlyName().ToStdString() ) );
116 return tl::unexpected( e );
117 }
118
119 std::unique_ptr<EDA_ITEM> created = CreateItemForType( aType, aContainer );
120
121 if( !created )
122 {
123 ApiResponseStatus e;
124 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
125 e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled",
126 magic_enum::enum_name( aType ) ) );
127 return tl::unexpected( e );
128 }
129
130 return created;
131}
132
133
135 const std::string& aClientName,
136 const types::ItemHeader &aHeader,
137 const google::protobuf::RepeatedPtrField<google::protobuf::Any>& aItems,
138 std::function<void( ItemStatus, google::protobuf::Any )> aItemHandler )
139{
140 ApiResponseStatus e;
141
142 auto containerResult = validateItemHeaderDocument( aHeader );
143
144 if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED )
145 {
146 // No message needed for AS_UNHANDLED; this is an internal flag for the API server
147 e.set_status( ApiStatusCode::AS_UNHANDLED );
148 return tl::unexpected( e );
149 }
150 else if( !containerResult )
151 {
152 e.CopyFrom( containerResult.error() );
153 return tl::unexpected( e );
154 }
155
156 SCH_SCREEN* screen = m_frame->GetScreen();
157 EE_RTREE& screenItems = screen->Items();
158
159 std::map<KIID, EDA_ITEM*> itemUuidMap;
160
161 std::for_each( screenItems.begin(), screenItems.end(),
162 [&]( EDA_ITEM* aItem )
163 {
164 itemUuidMap[aItem->m_Uuid] = aItem;
165 } );
166
167 EDA_ITEM* container = nullptr;
168
169 if( containerResult->has_value() )
170 {
171 const KIID& containerId = **containerResult;
172
173 if( itemUuidMap.count( containerId ) )
174 {
175 container = itemUuidMap.at( containerId );
176
177 if( !container )
178 {
179 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
180 e.set_error_message( fmt::format(
181 "The requested container {} is not a valid schematic item container",
182 containerId.AsStdString() ) );
183 return tl::unexpected( e );
184 }
185 }
186 else
187 {
188 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
189 e.set_error_message( fmt::format(
190 "The requested container {} does not exist in this document",
191 containerId.AsStdString() ) );
192 return tl::unexpected( e );
193 }
194 }
195
196 COMMIT* commit = getCurrentCommit( aClientName );
197
198 for( const google::protobuf::Any& anyItem : aItems )
199 {
200 ItemStatus status;
201 std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
202
203 if( !type )
204 {
205 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
206 status.set_error_message( fmt::format( "Could not decode a valid type from {}",
207 anyItem.type_url() ) );
208 aItemHandler( status, anyItem );
209 continue;
210 }
211
213 createItemForType( *type, container );
214
215 if( !creationResult )
216 {
217 status.set_code( ItemStatusCode::ISC_INVALID_TYPE );
218 status.set_error_message( creationResult.error().error_message() );
219 aItemHandler( status, anyItem );
220 continue;
221 }
222
223 std::unique_ptr<EDA_ITEM> item( std::move( *creationResult ) );
224
225 if( !item->Deserialize( anyItem ) )
226 {
227 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
228 e.set_error_message( fmt::format( "could not unpack {} from request",
229 item->GetClass().ToStdString() ) );
230 return tl::unexpected( e );
231 }
232
233 if( aCreate && itemUuidMap.count( item->m_Uuid ) )
234 {
235 status.set_code( ItemStatusCode::ISC_EXISTING );
236 status.set_error_message( fmt::format( "an item with UUID {} already exists",
237 item->m_Uuid.AsStdString() ) );
238 aItemHandler( status, anyItem );
239 continue;
240 }
241 else if( !aCreate && !itemUuidMap.count( item->m_Uuid ) )
242 {
243 status.set_code( ItemStatusCode::ISC_NONEXISTENT );
244 status.set_error_message( fmt::format( "an item with UUID {} does not exist",
245 item->m_Uuid.AsStdString() ) );
246 aItemHandler( status, anyItem );
247 continue;
248 }
249
250 status.set_code( ItemStatusCode::ISC_OK );
251 google::protobuf::Any newItem;
252
253 if( aCreate )
254 {
255 item->Serialize( newItem );
256 commit->Add( item.release() );
257
258 if( !m_activeClients.count( aClientName ) )
259 pushCurrentCommit( aClientName, _( "Added items via API" ) );
260 }
261 else
262 {
263 EDA_ITEM* edaItem = itemUuidMap[item->m_Uuid];
264
265 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( edaItem ) )
266 {
267 schItem->SwapData( static_cast<SCH_ITEM*>( item.get() ) );
268 schItem->Serialize( newItem );
269 commit->Modify( schItem );
270 }
271 else
272 {
273 wxASSERT( false );
274 }
275
276 if( !m_activeClients.count( aClientName ) )
277 pushCurrentCommit( aClientName, _( "Created items via API" ) );
278 }
279
280 aItemHandler( status, newItem );
281 }
282
283
284 return ItemRequestStatus::IRS_OK;
285}
286
287
288void API_HANDLER_SCH::deleteItemsInternal( std::map<KIID, ItemDeletionStatus>& aItemsToDelete,
289 const std::string& aClientName )
290{
291 // TODO
292}
293
294
295std::optional<EDA_ITEM*> API_HANDLER_SCH::getItemFromDocument( const DocumentSpecifier& aDocument,
296 const KIID& aId )
297{
298 if( !validateDocument( aDocument ) )
299 return std::nullopt;
300
301 // TODO
302
303 return std::nullopt;
304}
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)
Modify a given item in the model.
Definition: commit.h:108
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
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:252
Holds all the data relating to one schematic.
Definition: schematic.h:82
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:167
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:108
Schematic symbol object.
Definition: sch_symbol.h:77
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