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
67
68
71{
72 GetVersionResponse reply;
73
74 reply.mutable_version()->set_full_version( GetBuildVersion().ToStdString() );
75
76 std::tuple<int, int, int> version = GetMajorMinorPatchTuple();
77 reply.mutable_version()->set_major( std::get<0>( version ) );
78 reply.mutable_version()->set_minor( std::get<1>( version ) );
79 reply.mutable_version()->set_patch( std::get<2>( version ) );
80
81 return reply;
82}
83
84
87{
88 wxFileName fn( wxEmptyString, wxString::FromUTF8( aCtx.Request.binary_name() ) );
89#ifdef _WIN32
90 fn.SetExt( wxT( "exe" ) );
91#endif
92
93 wxString path = FindKicadFile( fn.GetFullName() );
94 PathResponse reply;
95 reply.set_path( path.ToUTF8() );
96 return reply;
97}
98
99
102{
103 NetClassesResponse reply;
104
105 std::shared_ptr<NET_SETTINGS>& netSettings =
107
108 google::protobuf::Any any;
109
110 netSettings->GetDefaultNetclass()->Serialize( any );
111 any.UnpackTo( reply.add_net_classes() );
112
113 for( const auto& netClass : netSettings->GetNetclasses() | std::views::values )
114 {
115 netClass->Serialize( any );
116 any.UnpackTo( reply.add_net_classes() );
117 }
118
119 return reply;
120}
121
122
125{
126 std::shared_ptr<NET_SETTINGS>& netSettings =
128
129 if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
130 netSettings->ClearNetclasses();
131
132 auto netClasses = netSettings->GetNetclasses();
133 google::protobuf::Any any;
134
135 for( const auto& ncProto : aCtx.Request.net_classes() )
136 {
137 any.PackFrom( ncProto );
138 wxString name = wxString::FromUTF8( ncProto.name() );
139
140 if( name == wxT( "Default" ) )
141 {
142 netSettings->GetDefaultNetclass()->Deserialize( any );
143 }
144 else
145 {
146 if( !netClasses.contains( name ) )
147 netClasses.insert( { name, std::make_shared<NETCLASS>( name, false ) } );
148
149 netClasses[name]->Deserialize( any );
150 }
151 }
152
153 netSettings->SetNetclasses( netClasses );
154
155 return Empty();
156}
157
158
163
164
167{
169 google::protobuf::Any any;
170 any.PackFrom( aCtx.Request.text() );
171
172 if( !text.Deserialize( any ) )
173 {
174 ApiResponseStatus e;
175 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
176 e.set_error_message( "Could not decode text in GetTextExtents message" );
177 return tl::unexpected( e );
178 }
179
180 types::Box2 response;
181
182 BOX2I bbox = text.GetTextBox( nullptr );
183 EDA_ANGLE angle = text.GetTextAngle();
184
185 if( !angle.IsZero() )
186 bbox = bbox.GetBoundingBoxRotated( text.GetTextPos(), text.GetTextAngle() );
187
188 response.mutable_position()->set_x_nm( bbox.GetPosition().x );
189 response.mutable_position()->set_y_nm( bbox.GetPosition().y );
190 response.mutable_size()->set_x_nm( bbox.GetSize().x );
191 response.mutable_size()->set_y_nm( bbox.GetSize().y );
192
193 return response;
194}
195
196
199{
200 GetTextAsShapesResponse reply;
201
202 for( const TextOrTextBox& textMsg : aCtx.Request.text() )
203 {
204 Text dummyText;
205 const Text* textPtr = &textMsg.text();
206
207 if( textMsg.has_textbox() )
208 {
209 dummyText.set_text( textMsg.textbox().text() );
210 dummyText.mutable_attributes()->CopyFrom( textMsg.textbox().attributes() );
211 textPtr = &dummyText;
212 }
213
215 google::protobuf::Any any;
216 any.PackFrom( *textPtr );
217
218 if( !text.Deserialize( any ) )
219 {
220 ApiResponseStatus e;
221 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
222 e.set_error_message( "Could not decode text in GetTextAsShapes message" );
223 return tl::unexpected( e );
224 }
225
226 std::shared_ptr<SHAPE_COMPOUND> shapes = text.GetEffectiveTextShape( false );
227
228 TextWithShapes* entry = reply.add_text_with_shapes();
229 entry->mutable_text()->CopyFrom( textMsg );
230
231 for( SHAPE* subshape : shapes->Shapes() )
232 {
233 EDA_SHAPE proxy( *subshape );
234 proxy.Serialize( any );
235 GraphicShape* shapeMsg = entry->mutable_shapes()->add_shapes();
236 any.UnpackTo( shapeMsg );
237 }
238
239 if( textMsg.has_textbox() )
240 {
241 GraphicShape* border = entry->mutable_shapes()->add_shapes();
242 int width = textMsg.textbox().attributes().stroke_width().value_nm();
243 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
244 VECTOR2I tl = UnpackVector2( textMsg.textbox().top_left() );
245 VECTOR2I br = UnpackVector2( textMsg.textbox().bottom_right() );
246
247 // top
248 PackVector2( *border->mutable_segment()->mutable_start(), tl );
249 PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( br.x, tl.y ) );
250
251 // right
252 border = entry->mutable_shapes()->add_shapes();
253 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
254 PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( br.x, tl.y ) );
255 PackVector2( *border->mutable_segment()->mutable_end(), br );
256
257 // bottom
258 border = entry->mutable_shapes()->add_shapes();
259 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
260 PackVector2( *border->mutable_segment()->mutable_start(), br );
261 PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( tl.x, br.y ) );
262
263 // left
264 border = entry->mutable_shapes()->add_shapes();
265 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
266 PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( tl.x, br.y ) );
267 PackVector2( *border->mutable_segment()->mutable_end(), tl );
268 }
269 }
270
271 return reply;
272}
273
274
277{
278 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
279 {
280 ApiResponseStatus e;
281 e.set_status( ApiStatusCode::AS_UNHANDLED );
282 // No error message, this is a flag that the server should try a different handler
283 return tl::unexpected( e );
284 }
285
286 ExpandTextVariablesResponse reply;
288
289 for( const std::string& textMsg : aCtx.Request.text() )
290 {
291 wxString result = ExpandTextVars( wxString::FromUTF8( textMsg ), &project );
292 reply.add_text( result.ToUTF8() );
293 }
294
295 return reply;
296}
297
298
301{
302 wxString identifier = wxString::FromUTF8( aCtx.Request.identifier() );
303
304 if( identifier.IsEmpty() )
305 {
306 ApiResponseStatus e;
307 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
308 e.set_error_message( "plugin identifier is missing" );
309 return tl::unexpected( e );
310 }
311
312 if( API_PLUGIN::IsValidIdentifier( identifier ) )
313 {
314 ApiResponseStatus e;
315 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
316 e.set_error_message( "plugin identifier is invalid" );
317 return tl::unexpected( e );
318 }
319
320 wxFileName path( PATHS::GetUserSettingsPath(), wxEmptyString );
321 path.AppendDir( "plugins" );
322
323 // Create the base plugins path if needed, but leave the specific plugin to create its own path
324 PATHS::EnsurePathExists( path.GetPath() );
325
326 path.AppendDir( identifier );
327
328 StringResponse reply;
329 reply.set_response( path.GetPath() );
330 return reply;
331}
332
333
336{
337 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
338 {
339 ApiResponseStatus e;
340 e.set_status( ApiStatusCode::AS_UNHANDLED );
341 // No error message, this is a flag that the server should try a different handler
342 return tl::unexpected( e );
343 }
344
346
347 if( project.IsNullProject() )
348 {
349 ApiResponseStatus e;
350 e.set_status( ApiStatusCode::AS_NOT_READY );
351 e.set_error_message( "no valid project is loaded, cannot get text variables" );
352 return tl::unexpected( e );
353 }
354
355 const std::map<wxString, wxString>& vars = project.GetTextVars();
356
357 project::TextVariables reply;
358 auto map = reply.mutable_variables();
359
360 for( const auto& [key, value] : vars )
361 ( *map )[ std::string( key.ToUTF8() ) ] = value.ToUTF8();
362
363 return reply;
364}
365
366
369{
370 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
371 {
372 ApiResponseStatus e;
373 e.set_status( ApiStatusCode::AS_UNHANDLED );
374 // No error message, this is a flag that the server should try a different handler
375 return tl::unexpected( e );
376 }
377
379
380 if( project.IsNullProject() )
381 {
382 ApiResponseStatus e;
383 e.set_status( ApiStatusCode::AS_NOT_READY );
384 e.set_error_message( "no valid project is loaded, cannot set text variables" );
385 return tl::unexpected( e );
386 }
387
388 const project::TextVariables& newVars = aCtx.Request.variables();
389 std::map<wxString, wxString>& vars = project.GetTextVars();
390
391 if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
392 vars.clear();
393
394 for( const auto& [key, value] : newVars.variables() )
395 vars[wxString::FromUTF8( key )] = wxString::FromUTF8( value );
396
398
399 return Empty();
400}
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)
HANDLER_RESULT< Empty > handleSetNetClasses(const HANDLER_CONTEXT< commands::SetNetClasses > &aCtx)
HANDLER_RESULT< commands::StringResponse > handleGetPluginSettingsPath(const HANDLER_CONTEXT< commands::GetPluginSettingsPath > &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:80
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
Definition paths.cpp:508
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition paths.cpp:624
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:131
std::shared_ptr< NET_SETTINGS > m_NetSettings
Net settings for this project (owned here)
Container for project specific data.
Definition project.h:65
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:203
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:695