KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_handler_common.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 along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <ranges>
22#include <tuple>
23
25#include <build_version.h>
26#include <eda_shape.h>
27#include <eda_text.h>
28#include <gestfich.h>
30#include <google/protobuf/empty.pb.h>
31#include <paths.h>
32#include <pgm_base.h>
33#include <api/api_plugin.h>
34#include <api/api_utils.h>
38#include <wx/string.h>
39
40using namespace kiapi::common::commands;
41using namespace kiapi::common::types;
42using google::protobuf::Empty;
43
44
71
72
75{
76 GetVersionResponse reply;
77
78 reply.mutable_version()->set_full_version( GetBuildVersion().ToStdString() );
79
80 std::tuple<int, int, int> version = GetMajorMinorPatchTuple();
81 reply.mutable_version()->set_major( std::get<0>( version ) );
82 reply.mutable_version()->set_minor( std::get<1>( version ) );
83 reply.mutable_version()->set_patch( std::get<2>( version ) );
84
85 return reply;
86}
87
88
91{
92 wxFileName fn( wxEmptyString, wxString::FromUTF8( aCtx.Request.binary_name() ) );
93#ifdef _WIN32
94 fn.SetExt( wxT( "exe" ) );
95#endif
96
97 wxString path = FindKicadFile( fn.GetFullName() );
98 PathResponse reply;
99 reply.set_path( path.ToUTF8() );
100 return reply;
101}
102
103
106{
107 NetClassesResponse reply;
108
109 std::shared_ptr<NET_SETTINGS>& netSettings =
111
112 google::protobuf::Any any;
113
114 netSettings->GetDefaultNetclass()->Serialize( any );
115 any.UnpackTo( reply.add_net_classes() );
116
117 for( const auto& netClass : netSettings->GetNetclasses() | std::views::values )
118 {
119 netClass->Serialize( any );
120 any.UnpackTo( reply.add_net_classes() );
121 }
122
123 return reply;
124}
125
126
129{
130 std::shared_ptr<NET_SETTINGS>& netSettings =
132
133 if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
134 netSettings->ClearNetclasses();
135
136 auto netClasses = netSettings->GetNetclasses();
137 google::protobuf::Any any;
138
139 for( const auto& ncProto : aCtx.Request.net_classes() )
140 {
141 any.PackFrom( ncProto );
142 wxString name = wxString::FromUTF8( ncProto.name() );
143
144 if( name == wxT( "Default" ) )
145 {
146 netSettings->GetDefaultNetclass()->Deserialize( any );
147 }
148 else
149 {
150 if( !netClasses.contains( name ) )
151 netClasses.insert( { name, std::make_shared<NETCLASS>( name, false ) } );
152
153 netClasses[name]->Deserialize( any );
154 }
155 }
156
157 netSettings->SetNetclasses( netClasses );
158
159 return Empty();
160}
161
162
167
168
171{
173 google::protobuf::Any any;
174 any.PackFrom( aCtx.Request.text() );
175
176 if( !text.Deserialize( any ) )
177 {
178 ApiResponseStatus e;
179 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
180 e.set_error_message( "Could not decode text in GetTextExtents message" );
181 return tl::unexpected( e );
182 }
183
184 types::Box2 response;
185
186 BOX2I bbox = text.GetTextBox( nullptr );
187 EDA_ANGLE angle = text.GetTextAngle();
188
189 if( !angle.IsZero() )
190 bbox = bbox.GetBoundingBoxRotated( text.GetTextPos(), text.GetTextAngle() );
191
192 response.mutable_position()->set_x_nm( bbox.GetPosition().x );
193 response.mutable_position()->set_y_nm( bbox.GetPosition().y );
194 response.mutable_size()->set_x_nm( bbox.GetSize().x );
195 response.mutable_size()->set_y_nm( bbox.GetSize().y );
196
197 return response;
198}
199
200
203{
204 GetTextAsShapesResponse reply;
205
206 for( const TextOrTextBox& textMsg : aCtx.Request.text() )
207 {
208 Text dummyText;
209 const Text* textPtr = &textMsg.text();
210
211 if( textMsg.has_textbox() )
212 {
213 dummyText.set_text( textMsg.textbox().text() );
214 dummyText.mutable_attributes()->CopyFrom( textMsg.textbox().attributes() );
215 textPtr = &dummyText;
216 }
217
219 google::protobuf::Any any;
220 any.PackFrom( *textPtr );
221
222 if( !text.Deserialize( any ) )
223 {
224 ApiResponseStatus e;
225 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
226 e.set_error_message( "Could not decode text in GetTextAsShapes message" );
227 return tl::unexpected( e );
228 }
229
230 std::shared_ptr<SHAPE_COMPOUND> shapes = text.GetEffectiveTextShape( false );
231
232 TextWithShapes* entry = reply.add_text_with_shapes();
233 entry->mutable_text()->CopyFrom( textMsg );
234
235 for( SHAPE* subshape : shapes->Shapes() )
236 {
237 EDA_SHAPE proxy( *subshape );
238 proxy.Serialize( any );
239 GraphicShape* shapeMsg = entry->mutable_shapes()->add_shapes();
240 any.UnpackTo( shapeMsg );
241 }
242
243 if( textMsg.has_textbox() )
244 {
245 GraphicShape* border = entry->mutable_shapes()->add_shapes();
246 int width = textMsg.textbox().attributes().stroke_width().value_nm();
247 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
248 VECTOR2I tl = UnpackVector2( textMsg.textbox().top_left() );
249 VECTOR2I br = UnpackVector2( textMsg.textbox().bottom_right() );
250
251 // top
252 PackVector2( *border->mutable_segment()->mutable_start(), tl );
253 PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( br.x, tl.y ) );
254
255 // right
256 border = entry->mutable_shapes()->add_shapes();
257 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
258 PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( br.x, tl.y ) );
259 PackVector2( *border->mutable_segment()->mutable_end(), br );
260
261 // bottom
262 border = entry->mutable_shapes()->add_shapes();
263 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
264 PackVector2( *border->mutable_segment()->mutable_start(), br );
265 PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( tl.x, br.y ) );
266
267 // left
268 border = entry->mutable_shapes()->add_shapes();
269 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
270 PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( tl.x, br.y ) );
271 PackVector2( *border->mutable_segment()->mutable_end(), tl );
272 }
273 }
274
275 return reply;
276}
277
278
281{
282 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
283 {
284 ApiResponseStatus e;
285 e.set_status( ApiStatusCode::AS_UNHANDLED );
286 // No error message, this is a flag that the server should try a different handler
287 return tl::unexpected( e );
288 }
289
290 ExpandTextVariablesResponse reply;
292
293 for( const std::string& textMsg : aCtx.Request.text() )
294 {
295 wxString result = ExpandTextVars( wxString::FromUTF8( textMsg ), &project );
296 reply.add_text( result.ToUTF8() );
297 }
298
299 return reply;
300}
301
302
305{
306 wxString identifier = wxString::FromUTF8( aCtx.Request.identifier() );
307
308 if( identifier.IsEmpty() )
309 {
310 ApiResponseStatus e;
311 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
312 e.set_error_message( "plugin identifier is missing" );
313 return tl::unexpected( e );
314 }
315
316 if( API_PLUGIN::IsValidIdentifier( identifier ) )
317 {
318 ApiResponseStatus e;
319 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
320 e.set_error_message( "plugin identifier is invalid" );
321 return tl::unexpected( e );
322 }
323
324 wxFileName path( PATHS::GetUserSettingsPath(), wxEmptyString );
325 path.AppendDir( "plugins" );
326
327 // Create the base plugins path if needed, but leave the specific plugin to create its own path
328 PATHS::EnsurePathExists( path.GetPath() );
329
330 path.AppendDir( identifier );
331
332 StringResponse reply;
333 reply.set_response( path.GetPath() );
334 return reply;
335}
336
337
340{
341 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
342 {
343 ApiResponseStatus e;
344 e.set_status( ApiStatusCode::AS_UNHANDLED );
345 // No error message, this is a flag that the server should try a different handler
346 return tl::unexpected( e );
347 }
348
350
351 if( project.IsNullProject() )
352 {
353 ApiResponseStatus e;
354 e.set_status( ApiStatusCode::AS_NOT_READY );
355 e.set_error_message( "no valid project is loaded, cannot get text variables" );
356 return tl::unexpected( e );
357 }
358
359 const std::map<wxString, wxString>& vars = project.GetTextVars();
360
361 project::TextVariables reply;
362 auto map = reply.mutable_variables();
363
364 for( const auto& [key, value] : vars )
365 ( *map )[ std::string( key.ToUTF8() ) ] = value.ToUTF8();
366
367 return reply;
368}
369
370
373{
374 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
375 {
376 ApiResponseStatus e;
377 e.set_status( ApiStatusCode::AS_UNHANDLED );
378 // No error message, this is a flag that the server should try a different handler
379 return tl::unexpected( e );
380 }
381
383
384 if( project.IsNullProject() )
385 {
386 ApiResponseStatus e;
387 e.set_status( ApiStatusCode::AS_NOT_READY );
388 e.set_error_message( "no valid project is loaded, cannot set text variables" );
389 return tl::unexpected( e );
390 }
391
392 const project::TextVariables& newVars = aCtx.Request.variables();
393 std::map<wxString, wxString>& vars = project.GetTextVars();
394
395 if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
396 vars.clear();
397
398 for( const auto& [key, value] : newVars.variables() )
399 vars[wxString::FromUTF8( key )] = wxString::FromUTF8( value );
400
402
403 return Empty();
404}
405
406
409{
411 {
412 ApiResponseStatus e;
413 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
414 e.set_error_message( "OpenDocument is not available in this KiCad mode" );
415 return tl::unexpected( e );
416 }
417
418 return m_openDocumentHandler( aCtx.Request );
419}
420
421
424{
426 {
427 ApiResponseStatus e;
428 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
429 e.set_error_message( "CloseDocument is not available in this KiCad mode" );
430 return tl::unexpected( e );
431 }
432
433 return m_closeDocumentHandler( aCtx.Request );
434}
const char * name
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition api_handler.h:45
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
const std::tuple< int, int, int > & GetMajorMinorPatchTuple()
Get the build version numbers as a tuple.
wxString GetBuildVersion()
Get the full KiCad version string.
HANDLER_RESULT< Empty > handlePing(const HANDLER_CONTEXT< commands::Ping > &aCtx)
HANDLER_RESULT< types::Box2 > handleGetTextExtents(const HANDLER_CONTEXT< commands::GetTextExtents > &aCtx)
HANDLER_RESULT< commands::GetTextAsShapesResponse > handleGetTextAsShapes(const HANDLER_CONTEXT< commands::GetTextAsShapes > &aCtx)
HANDLER_RESULT< commands::GetVersionResponse > handleGetVersion(const HANDLER_CONTEXT< commands::GetVersion > &aCtx)
HANDLER_RESULT< commands::PathResponse > handleGetKiCadBinaryPath(const HANDLER_CONTEXT< commands::GetKiCadBinaryPath > &aCtx)
HANDLER_RESULT< commands::NetClassesResponse > handleGetNetClasses(const HANDLER_CONTEXT< commands::GetNetClasses > &aCtx)
CLOSE_DOCUMENT_HANDLER m_closeDocumentHandler
OPEN_DOCUMENT_HANDLER m_openDocumentHandler
HANDLER_RESULT< Empty > handleCloseDocument(const HANDLER_CONTEXT< commands::CloseDocument > &aCtx)
HANDLER_RESULT< Empty > handleSetNetClasses(const HANDLER_CONTEXT< commands::SetNetClasses > &aCtx)
HANDLER_RESULT< commands::StringResponse > handleGetPluginSettingsPath(const HANDLER_CONTEXT< commands::GetPluginSettingsPath > &aCtx)
HANDLER_RESULT< commands::OpenDocumentResponse > handleOpenDocument(const HANDLER_CONTEXT< commands::OpenDocument > &aCtx)
HANDLER_RESULT< commands::ExpandTextVariablesResponse > handleExpandTextVariables(const HANDLER_CONTEXT< commands::ExpandTextVariables > &aCtx)
HANDLER_RESULT< Empty > handleSetTextVariables(const HANDLER_CONTEXT< commands::SetTextVariables > &aCtx)
HANDLER_RESULT< project::TextVariables > handleGetTextVariables(const HANDLER_CONTEXT< commands::GetTextVariables > &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 bool IsValidIdentifier(const wxString &aIdentifier)
constexpr const Vec & GetPosition() const
Definition box2.h:211
const BOX2< Vec > GetBoundingBoxRotated(const VECTOR2I &aRotCenter, const EDA_ANGLE &aAngle) const
Useful to calculate bounding box of rotated items, when rotation is not cardinal.
Definition box2.h:720
constexpr const SizeVec & GetSize() const
Definition box2.h:206
bool IsZero() const
Definition eda_angle.h:136
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:91
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
Definition paths.cpp:518
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition paths.cpp:634
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:130
std::shared_ptr< NET_SETTINGS > m_NetSettings
Net settings for this project (owned here)
Container for project specific data.
Definition project.h:66
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:204
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Save a loaded project.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
An abstract shape on 2D plane.
Definition shape.h:126
A type-safe container of any type.
Definition ki_any.h:93
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:62
wxString FindKicadFile(const wxString &shortname)
Search the executable file shortname in KiCad binary path and return full file name if found or short...
Definition gestfich.cpp:61
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput)
Definition api_utils.cpp:86
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition api_utils.cpp:79
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
RequestMessageType Request
Definition api_handler.h:52
std::string path
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687