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 <tuple>
22
24#include <build_version.h>
25#include <eda_shape.h>
26#include <eda_text.h>
27#include <gestfich.h>
29#include <google/protobuf/empty.pb.h>
30#include <paths.h>
31#include <pgm_base.h>
32#include <api/api_plugin.h>
33#include <api/api_utils.h>
37#include <wx/string.h>
38
39using namespace kiapi::common::commands;
40using namespace kiapi::common::types;
41using google::protobuf::Empty;
42
43
46{
47 registerHandler<commands::GetVersion, GetVersionResponse>( &API_HANDLER_COMMON::handleGetVersion );
48 registerHandler<GetKiCadBinaryPath, PathResponse>(
50 registerHandler<GetNetClasses, NetClassesResponse>( &API_HANDLER_COMMON::handleGetNetClasses );
51 registerHandler<SetNetClasses, Empty>( &API_HANDLER_COMMON::handleSetNetClasses );
52 registerHandler<Ping, Empty>( &API_HANDLER_COMMON::handlePing );
53 registerHandler<GetTextExtents, types::Box2>( &API_HANDLER_COMMON::handleGetTextExtents );
54 registerHandler<GetTextAsShapes, GetTextAsShapesResponse>(
56 registerHandler<ExpandTextVariables, ExpandTextVariablesResponse>(
58 registerHandler<GetPluginSettingsPath, StringResponse>(
60 registerHandler<GetTextVariables, project::TextVariables>(
62 registerHandler<SetTextVariables, Empty>(
64
65}
66
67
70{
71 GetVersionResponse reply;
72
73 reply.mutable_version()->set_full_version( GetBuildVersion().ToStdString() );
74
75 std::tuple<int, int, int> version = GetMajorMinorPatchTuple();
76 reply.mutable_version()->set_major( std::get<0>( version ) );
77 reply.mutable_version()->set_minor( std::get<1>( version ) );
78 reply.mutable_version()->set_patch( std::get<2>( version ) );
79
80 return reply;
81}
82
83
86{
87 wxFileName fn( wxEmptyString, wxString::FromUTF8( aCtx.Request.binary_name() ) );
88#ifdef _WIN32
89 fn.SetExt( wxT( "exe" ) );
90#endif
91
92 wxString path = FindKicadFile( fn.GetFullName() );
93 PathResponse reply;
94 reply.set_path( path.ToUTF8() );
95 return reply;
96}
97
98
101{
102 NetClassesResponse reply;
103
104 std::shared_ptr<NET_SETTINGS>& netSettings =
106
107 google::protobuf::Any any;
108
109 netSettings->GetDefaultNetclass()->Serialize( any );
110 any.UnpackTo( reply.add_net_classes() );
111
112 for( const auto& netClass : netSettings->GetNetclasses() | std::views::values )
113 {
114 netClass->Serialize( any );
115 any.UnpackTo( reply.add_net_classes() );
116 }
117
118 return reply;
119}
120
121
124{
125 std::shared_ptr<NET_SETTINGS>& netSettings =
127
128 if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
129 netSettings->ClearNetclasses();
130
131 auto netClasses = netSettings->GetNetclasses();
132 google::protobuf::Any any;
133
134 for( const auto& ncProto : aCtx.Request.net_classes() )
135 {
136 any.PackFrom( ncProto );
137 wxString name = wxString::FromUTF8( ncProto.name() );
138
139 if( name == wxT( "Default" ) )
140 {
141 netSettings->GetDefaultNetclass()->Deserialize( any );
142 }
143 else
144 {
145 if( !netClasses.contains( name ) )
146 netClasses.insert( { name, std::make_shared<NETCLASS>( name, false ) } );
147
148 netClasses[name]->Deserialize( any );
149 }
150 }
151
152 netSettings->SetNetclasses( netClasses );
153
154 return Empty();
155}
156
157
159{
160 return Empty();
161}
162
163
166{
168 google::protobuf::Any any;
169 any.PackFrom( aCtx.Request.text() );
170
171 if( !text.Deserialize( any ) )
172 {
173 ApiResponseStatus e;
174 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
175 e.set_error_message( "Could not decode text in GetTextExtents message" );
176 return tl::unexpected( e );
177 }
178
179 types::Box2 response;
180
181 BOX2I bbox = text.GetTextBox();
182 EDA_ANGLE angle = text.GetTextAngle();
183
184 if( !angle.IsZero() )
185 bbox = bbox.GetBoundingBoxRotated( text.GetTextPos(), text.GetTextAngle() );
186
187 response.mutable_position()->set_x_nm( bbox.GetPosition().x );
188 response.mutable_position()->set_y_nm( bbox.GetPosition().y );
189 response.mutable_size()->set_x_nm( bbox.GetSize().x );
190 response.mutable_size()->set_y_nm( bbox.GetSize().y );
191
192 return response;
193}
194
195
198{
199 GetTextAsShapesResponse reply;
200
201 for( const TextOrTextBox& textMsg : aCtx.Request.text() )
202 {
203 Text dummyText;
204 const Text* textPtr = &textMsg.text();
205
206 if( textMsg.has_textbox() )
207 {
208 dummyText.set_text( textMsg.textbox().text() );
209 dummyText.mutable_attributes()->CopyFrom( textMsg.textbox().attributes() );
210 textPtr = &dummyText;
211 }
212
214 google::protobuf::Any any;
215 any.PackFrom( *textPtr );
216
217 if( !text.Deserialize( any ) )
218 {
219 ApiResponseStatus e;
220 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
221 e.set_error_message( "Could not decode text in GetTextAsShapes message" );
222 return tl::unexpected( e );
223 }
224
225 std::shared_ptr<SHAPE_COMPOUND> shapes = text.GetEffectiveTextShape( false );
226
227 TextWithShapes* entry = reply.add_text_with_shapes();
228 entry->mutable_text()->CopyFrom( textMsg );
229
230 for( SHAPE* subshape : shapes->Shapes() )
231 {
232 EDA_SHAPE proxy( *subshape );
233 proxy.Serialize( any );
234 GraphicShape* shapeMsg = entry->mutable_shapes()->add_shapes();
235 any.UnpackTo( shapeMsg );
236 }
237
238 if( textMsg.has_textbox() )
239 {
240 GraphicShape* border = entry->mutable_shapes()->add_shapes();
241 int width = textMsg.textbox().attributes().stroke_width().value_nm();
242 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
243 VECTOR2I tl = UnpackVector2( textMsg.textbox().top_left() );
244 VECTOR2I br = UnpackVector2( textMsg.textbox().bottom_right() );
245
246 // top
247 PackVector2( *border->mutable_segment()->mutable_start(), tl );
248 PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( br.x, tl.y ) );
249
250 // right
251 border = entry->mutable_shapes()->add_shapes();
252 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
253 PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( br.x, tl.y ) );
254 PackVector2( *border->mutable_segment()->mutable_end(), br );
255
256 // bottom
257 border = entry->mutable_shapes()->add_shapes();
258 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
259 PackVector2( *border->mutable_segment()->mutable_start(), br );
260 PackVector2( *border->mutable_segment()->mutable_end(), VECTOR2I( tl.x, br.y ) );
261
262 // left
263 border = entry->mutable_shapes()->add_shapes();
264 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
265 PackVector2( *border->mutable_segment()->mutable_start(), VECTOR2I( tl.x, br.y ) );
266 PackVector2( *border->mutable_segment()->mutable_end(), tl );
267 }
268 }
269
270 return reply;
271}
272
273
276{
277 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
278 {
279 ApiResponseStatus e;
280 e.set_status( ApiStatusCode::AS_UNHANDLED );
281 // No error message, this is a flag that the server should try a different handler
282 return tl::unexpected( e );
283 }
284
285 ExpandTextVariablesResponse reply;
287
288 for( const std::string& textMsg : aCtx.Request.text() )
289 {
290 wxString result = ExpandTextVars( wxString::FromUTF8( textMsg ), &project );
291 reply.add_text( result.ToUTF8() );
292 }
293
294 return reply;
295}
296
297
300{
301 wxString identifier = wxString::FromUTF8( aCtx.Request.identifier() );
302
303 if( identifier.IsEmpty() )
304 {
305 ApiResponseStatus e;
306 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
307 e.set_error_message( "plugin identifier is missing" );
308 return tl::unexpected( e );
309 }
310
311 if( API_PLUGIN::IsValidIdentifier( identifier ) )
312 {
313 ApiResponseStatus e;
314 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
315 e.set_error_message( "plugin identifier is invalid" );
316 return tl::unexpected( e );
317 }
318
319 wxFileName path( PATHS::GetUserSettingsPath(), wxEmptyString );
320 path.AppendDir( "plugins" );
321
322 // Create the base plugins path if needed, but leave the specific plugin to create its own path
323 PATHS::EnsurePathExists( path.GetPath() );
324
325 path.AppendDir( identifier );
326
327 StringResponse reply;
328 reply.set_response( path.GetPath() );
329 return reply;
330}
331
332
335{
336 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
337 {
338 ApiResponseStatus e;
339 e.set_status( ApiStatusCode::AS_UNHANDLED );
340 // No error message, this is a flag that the server should try a different handler
341 return tl::unexpected( e );
342 }
343
345
346 if( project.IsNullProject() )
347 {
348 ApiResponseStatus e;
349 e.set_status( ApiStatusCode::AS_NOT_READY );
350 e.set_error_message( "no valid project is loaded, cannot get text variables" );
351 return tl::unexpected( e );
352 }
353
354 const std::map<wxString, wxString>& vars = project.GetTextVars();
355
356 project::TextVariables reply;
357 auto map = reply.mutable_variables();
358
359 for( const auto& [key, value] : vars )
360 ( *map )[ std::string( key.ToUTF8() ) ] = value.ToUTF8();
361
362 return reply;
363}
364
365
368{
369 if( !aCtx.Request.has_document() || aCtx.Request.document().type() != DOCTYPE_PROJECT )
370 {
371 ApiResponseStatus e;
372 e.set_status( ApiStatusCode::AS_UNHANDLED );
373 // No error message, this is a flag that the server should try a different handler
374 return tl::unexpected( e );
375 }
376
378
379 if( project.IsNullProject() )
380 {
381 ApiResponseStatus e;
382 e.set_status( ApiStatusCode::AS_NOT_READY );
383 e.set_error_message( "no valid project is loaded, cannot set text variables" );
384 return tl::unexpected( e );
385 }
386
387 const project::TextVariables& newVars = aCtx.Request.variables();
388 std::map<wxString, wxString>& vars = project.GetTextVars();
389
390 if( aCtx.Request.merge_mode() == MapMergeMode::MMM_REPLACE )
391 vars.clear();
392
393 for( const auto& [key, value] : newVars.variables() )
394 vars[wxString::FromUTF8( key )] = wxString::FromUTF8( value );
395
397
398 return Empty();
399}
const char * name
Definition: DXF_plotter.cpp:59
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition: api_handler.h:45
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
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)
static bool IsValidIdentifier(const wxString &aIdentifier)
Definition: api_plugin.cpp:200
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:133
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: eda_shape.cpp:146
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:467
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition: paths.cpp:582
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:125
std::shared_ptr< NET_SETTINGS > m_NetSettings
Net settings for this project (owned here)
Definition: project_file.h:183
Container for project specific data.
Definition: project.h:64
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:200
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
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition: common.cpp:59
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:58
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput)
Definition: api_utils.cpp:84
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition: api_utils.cpp:77
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
RequestMessageType Request
Definition: api_handler.h:52