ADDED: HTTP library support
Fixes https://gitlab.com/kicad/code/kicad/-/issues/12027
This commit is contained in:
parent
b52c43b933
commit
2bcad18cc2
|
@ -561,6 +561,10 @@ set( COMMON_SRCS
|
|||
|
||||
database/database_connection.cpp
|
||||
database/database_lib_settings.cpp
|
||||
|
||||
http_lib/http_lib_connection.cpp
|
||||
http_lib/http_lib_settings.cpp
|
||||
|
||||
)
|
||||
|
||||
add_library( common STATIC
|
||||
|
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Andre F. K. Iwers <iwers11@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <wx/log.h>
|
||||
#include <fmt/core.h>
|
||||
#include <wx/translation.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <wx/base64.h>
|
||||
|
||||
#include <kicad_curl/kicad_curl_easy.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <http_lib/http_lib_connection.h>
|
||||
|
||||
const char* const traceHTTPLib = "KICAD_HTTP_LIB";
|
||||
|
||||
|
||||
HTTP_LIB_CONNECTION::HTTP_LIB_CONNECTION( const HTTP_LIB_SOURCE& aSource, bool aTestConnectionNow )
|
||||
{
|
||||
m_rootURL = aSource.root_url;
|
||||
m_token = aSource.token;
|
||||
m_sourceType = aSource.type;
|
||||
|
||||
if( aTestConnectionNow )
|
||||
{
|
||||
ValidateHTTPLibraryEndpoints();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HTTP_LIB_CONNECTION::~HTTP_LIB_CONNECTION()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
bool HTTP_LIB_CONNECTION::ValidateHTTPLibraryEndpoints()
|
||||
{
|
||||
std::string res = "";
|
||||
|
||||
std::unique_ptr<KICAD_CURL_EASY> m_curl = createCurlEasyObject();
|
||||
m_curl->SetURL( m_rootURL );
|
||||
|
||||
try
|
||||
{
|
||||
m_curl->Perform();
|
||||
|
||||
res = m_curl->GetBuffer();
|
||||
|
||||
if( !checkServerResponse( m_curl ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( res.length() == 0 )
|
||||
{
|
||||
m_lastError += wxString::Format( _( "KiCad received an empty response!" ) + "\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
nlohmann::json response = nlohmann::json::parse( res );
|
||||
|
||||
// Check that the endpoints exist, if not fail.
|
||||
if( !response.at( http_endpoint_categories ).empty()
|
||||
&& !response.at( http_endpoint_parts ).empty() )
|
||||
m_enpointValid = true;
|
||||
else
|
||||
m_enpointValid = false;
|
||||
}
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
m_lastError += wxString::Format( _( "Error: %s" ) + "\n" + _( "API Response: %s" ) + "\n",
|
||||
e.what(), res );
|
||||
|
||||
wxLogTrace( traceHTTPLib,
|
||||
wxT( "ValidateHTTPLibraryEndpoints: Exception occurred while testing the API "
|
||||
"connection: %s" ),
|
||||
m_lastError );
|
||||
|
||||
m_enpointValid = false;
|
||||
}
|
||||
|
||||
if( IsValidEnpoint() )
|
||||
{
|
||||
syncCategories();
|
||||
}
|
||||
|
||||
return IsValidEnpoint();
|
||||
}
|
||||
|
||||
|
||||
bool HTTP_LIB_CONNECTION::IsValidEnpoint() const
|
||||
{
|
||||
return m_enpointValid;
|
||||
}
|
||||
|
||||
|
||||
bool HTTP_LIB_CONNECTION::syncCategories()
|
||||
{
|
||||
if( !IsValidEnpoint() )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "syncCategories: without valid connection!" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string res = "";
|
||||
|
||||
std::unique_ptr<KICAD_CURL_EASY> m_curl = createCurlEasyObject();
|
||||
m_curl->SetURL( m_rootURL + http_endpoint_categories + ".json" );
|
||||
|
||||
try
|
||||
{
|
||||
m_curl->Perform();
|
||||
|
||||
res = m_curl->GetBuffer();
|
||||
|
||||
if( !checkServerResponse( m_curl ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nlohmann::json response = nlohmann::json::parse( res );
|
||||
|
||||
// collect the categories in vector
|
||||
for( const auto& item : response.items() )
|
||||
{
|
||||
HTTP_LIB_CATEGORY category;
|
||||
|
||||
category.id = item.value()["id"].get<std::string>();
|
||||
category.name = item.value()["name"].get<std::string>();
|
||||
|
||||
m_categories.push_back( category );
|
||||
}
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
m_lastError += wxString::Format( _( "Error: %s" ) + "\n" + _( "API Response: %s" ) + "\n",
|
||||
e.what(), res );
|
||||
|
||||
wxLogTrace( traceHTTPLib,
|
||||
wxT( "syncCategories: Exception occurred while syncing categories: %s" ),
|
||||
m_lastError );
|
||||
|
||||
m_categories.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool HTTP_LIB_CONNECTION::SelectOne( const std::string aPartID, HTTP_LIB_PART& aFetchedPart )
|
||||
{
|
||||
if( !IsValidEnpoint() )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "SelectOne: without valid connection!" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the same part is selected again, use cached part
|
||||
// instead to minimise http requests.
|
||||
if( m_cached_part.id == aPartID )
|
||||
{
|
||||
aFetchedPart = m_cached_part;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string res = "";
|
||||
|
||||
std::unique_ptr<KICAD_CURL_EASY> m_curl = createCurlEasyObject();
|
||||
m_curl->SetURL( m_rootURL + fmt::format( http_endpoint_parts + "/{}.json", aPartID ) );
|
||||
|
||||
try
|
||||
{
|
||||
m_curl->Perform();
|
||||
|
||||
res = m_curl->GetBuffer();
|
||||
|
||||
if( !checkServerResponse( m_curl ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nlohmann::json response = nlohmann::json::parse( res );
|
||||
std::string key = "";
|
||||
std::string value = "";
|
||||
|
||||
// the id used to identify the part, the name is needed to show a human-readable
|
||||
// part descirption to the user inside the symbol chooser dialog
|
||||
aFetchedPart.id = response.at( "id" );
|
||||
|
||||
// API might not want to return an optional name.
|
||||
if( response.contains( "name" ) )
|
||||
{
|
||||
aFetchedPart.name = response.at( "name" );
|
||||
}
|
||||
else
|
||||
{
|
||||
aFetchedPart.name = aFetchedPart.id;
|
||||
}
|
||||
|
||||
aFetchedPart.symbolIdStr = response.at( "symbolIdStr" );
|
||||
|
||||
// Extract available fields
|
||||
for( const auto& field : response.at( "fields" ).items() )
|
||||
{
|
||||
bool visible = true;
|
||||
|
||||
// name of the field
|
||||
key = field.key();
|
||||
|
||||
// this is a dict
|
||||
auto& properties = field.value();
|
||||
|
||||
value = properties.at( "value" );
|
||||
|
||||
// check if user wants to display field in schematic
|
||||
if( properties.contains( "visible" ) )
|
||||
{
|
||||
std::string buf = properties.at( "visible" );
|
||||
visible = boolFromString( buf );
|
||||
}
|
||||
|
||||
// Add field to fields list
|
||||
if( key.length() )
|
||||
{
|
||||
aFetchedPart.fields[key] = std::make_tuple( value, visible );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
m_lastError += wxString::Format( _( "Error: %s" ) + "\n" + _( "API Response: %s" ) + "\n",
|
||||
e.what(), res );
|
||||
|
||||
wxLogTrace( traceHTTPLib,
|
||||
wxT( "SelectOne: Exception occurred while retrieving part from REST API: %s" ),
|
||||
m_lastError );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cached_part = aFetchedPart;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool HTTP_LIB_CONNECTION::SelectAll( const HTTP_LIB_CATEGORY& aCategory,
|
||||
std::vector<HTTP_LIB_PART>& aParts )
|
||||
{
|
||||
if( !IsValidEnpoint() )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "SelectAll: without valid connection!" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string res = "";
|
||||
|
||||
std::unique_ptr<KICAD_CURL_EASY> m_curl = createCurlEasyObject();
|
||||
m_curl->SetURL( m_rootURL
|
||||
+ fmt::format( http_endpoint_parts + "/category/{}.json", aCategory.id ) );
|
||||
|
||||
try
|
||||
{
|
||||
m_curl->Perform();
|
||||
|
||||
res = m_curl->GetBuffer();
|
||||
|
||||
nlohmann::json response = nlohmann::json::parse( res );
|
||||
std::string key = "";
|
||||
std::string value = "";
|
||||
|
||||
for( nlohmann::json item : response )
|
||||
{
|
||||
//PART result;
|
||||
HTTP_LIB_PART part;
|
||||
|
||||
part.id = item.at( "id" );
|
||||
|
||||
// API might not want to return an optional name.
|
||||
if( item.contains( "name" ) )
|
||||
{
|
||||
part.name = item.at( "name" );
|
||||
}
|
||||
else
|
||||
{
|
||||
part.name = part.id;
|
||||
}
|
||||
|
||||
// add to cache
|
||||
m_cache[part.name] = std::make_tuple( part.id, aCategory.id );
|
||||
|
||||
aParts.emplace_back( std::move( part ) );
|
||||
}
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
m_lastError += wxString::Format( _( "Error: %s" ) + "\n" + _( "API Response: %s" ) + "\n",
|
||||
e.what(), res );
|
||||
|
||||
wxLogTrace( traceHTTPLib, wxT( "Exception occurred while syncing parts from REST API: %s" ),
|
||||
m_lastError );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool HTTP_LIB_CONNECTION::checkServerResponse( std::unique_ptr<KICAD_CURL_EASY>& m_curl )
|
||||
{
|
||||
|
||||
long http_code = 0;
|
||||
|
||||
curl_easy_getinfo( m_curl->GetCurl(), CURLINFO_RESPONSE_CODE, &http_code );
|
||||
|
||||
if( http_code != 200 )
|
||||
{
|
||||
m_lastError += wxString::Format( _( "API responded with error code: %s" ) + "\n",
|
||||
httpErrorCodeDescription( http_code ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool HTTP_LIB_CONNECTION::boolFromString( const std::any& aVal )
|
||||
{
|
||||
try
|
||||
{
|
||||
wxString strval( std::any_cast<std::string>( aVal ).c_str(), wxConvUTF8 );
|
||||
|
||||
if( strval.IsEmpty() )
|
||||
return true;
|
||||
|
||||
strval.MakeLower();
|
||||
|
||||
for( const auto& trueVal : { wxS( "true" ), wxS( "yes" ), wxS( "y" ), wxS( "1" ) } )
|
||||
{
|
||||
if( strval.Matches( trueVal ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
for( const auto& falseVal : { wxS( "false" ), wxS( "no" ), wxS( "n" ), wxS( "0" ) } )
|
||||
{
|
||||
if( strval.Matches( falseVal ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch( const std::bad_any_cast& )
|
||||
{
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* HTTP response status codes indicate whether a specific HTTP request has been successfully completed.
|
||||
* Responses are grouped in five classes:
|
||||
* Informational responses (100 ? 199)
|
||||
* Successful responses (200 ? 299)
|
||||
* Redirection messages (300 ? 399)
|
||||
* Client error responses (400 ? 499)
|
||||
* Server error responses (500 ? 599)
|
||||
*
|
||||
* see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
||||
*/
|
||||
wxString HTTP_LIB_CONNECTION::httpErrorCodeDescription( uint16_t http_code )
|
||||
{
|
||||
switch( http_code )
|
||||
{
|
||||
case 100: return "100" + _( "Continue" );
|
||||
case 101: return "101" + _( "Switching Protocols" );
|
||||
case 102: return "102" + _( "Processing" );
|
||||
case 103: return "103" + _( "Early Hints" );
|
||||
|
||||
case 200: return "200" + _( "OK" );
|
||||
case 201: return "201" + _( "Created" );
|
||||
case 203: return "203" + _( "Non-Authoritative Information" );
|
||||
case 204: return "204" + _( "No Content" );
|
||||
case 205: return "205" + _( "Reset Content" );
|
||||
case 206: return "206" + _( "Partial Content" );
|
||||
case 207: return "207" + _( "Multi-Status" );
|
||||
case 208: return "208" + _( "Already Reporte" );
|
||||
case 226: return "226" + _( "IM Used" );
|
||||
|
||||
case 300: return "300" + _( "Multiple Choices" );
|
||||
case 301: return "301" + _( "Moved Permanently" );
|
||||
case 302: return "302" + _( "Found" );
|
||||
case 303: return "303" + _( "See Other" );
|
||||
case 304: return "304" + _( "Not Modified" );
|
||||
case 305: return "305" + _( "Use Proxy (Deprecated)" );
|
||||
case 306: return "306" + _( "Unused" );
|
||||
case 307: return "307" + _( "Temporary Redirect" );
|
||||
case 308: return "308" + _( "Permanent Redirect" );
|
||||
|
||||
case 400: return "400" + _( "Bad Request" );
|
||||
case 401: return "401" + _( "Unauthorized" );
|
||||
case 402: return "402" + _( "Payment Required (Experimental)" );
|
||||
case 403: return "403" + _( "Forbidden" );
|
||||
case 404: return "404" + _( "Not Found" );
|
||||
case 405: return "405" + _( "Method Not Allowed" );
|
||||
case 406: return "406" + _( "Not Acceptable" );
|
||||
case 407: return "407" + _( "Proxy Authentication Required" );
|
||||
case 408: return "408" + _( "Request Timeout" );
|
||||
case 409: return "409" + _( "Conflict" );
|
||||
case 410: return "410" + _( "Gone" );
|
||||
case 411: return "411" + _( "Length Required" );
|
||||
case 412: return "413" + _( "Payload Too Large" );
|
||||
case 414: return "414" + _( "URI Too Long" );
|
||||
case 415: return "415" + _( "Unsupported Media Type" );
|
||||
case 416: return "416" + _( "Range Not Satisfiable" );
|
||||
case 417: return "417" + _( "Expectation Failed" );
|
||||
case 418: return "418" + _( "I'm a teapot" );
|
||||
case 421: return "421" + _( "Misdirected Request" );
|
||||
case 422: return "422" + _( "Unprocessable Conten" );
|
||||
case 423: return "423" + _( "Locked" );
|
||||
case 424: return "424" + _( "Failed Dependency" );
|
||||
case 425: return "425" + _( "Too Early (Experimental)" );
|
||||
case 426: return "426" + _( "Upgrade Required" );
|
||||
case 428: return "428" + _( "Precondition Required" );
|
||||
case 429: return "429" + _( "Too Many Requests" );
|
||||
case 431: return "431" + _( "Request Header Fields Too Large" );
|
||||
case 451: return "451" + _( "Unavailable For Legal Reasons" );
|
||||
|
||||
case 500: return "500" + _( "Internal Server Error" );
|
||||
case 501: return "501" + _( "Not Implemented" );
|
||||
case 502: return "502" + _( "Bad Gateway" );
|
||||
case 503: return "503" + _( "Service Unavailable" );
|
||||
case 504: return "504" + _( "Gateway Timeout" );
|
||||
case 505: return "505" + _( "HTTP Version Not Supported" );
|
||||
case 506: return "506" + _( "Variant Also Negotiates" );
|
||||
case 507: return "507" + _( "Insufficient Storag" );
|
||||
case 508: return "508" + _( "Loop Detecte" );
|
||||
case 510: return "510" + _( "Not Extended" );
|
||||
case 511: return "511" + _( "Network Authentication Required" );
|
||||
}
|
||||
|
||||
return wxString::Format( _( "Code Unkonwn: %d" ), http_code );
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Andre F. K. Iwers <iwers11@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <settings/parameters.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
|
||||
#include <http_lib/http_lib_settings.h>
|
||||
|
||||
const int httplibSchemaVersion = 1;
|
||||
|
||||
|
||||
HTTP_LIB_SETTINGS::HTTP_LIB_SETTINGS( const std::string& aFilename ) :
|
||||
JSON_SETTINGS( aFilename, SETTINGS_LOC::NONE, httplibSchemaVersion )
|
||||
{
|
||||
m_params.emplace_back( new PARAM<std::string>( "source.type", &sourceType, "" ) );
|
||||
|
||||
m_params.emplace_back(
|
||||
new PARAM<std::string>( "source.api_version", &m_Source.api_version, "" ) );
|
||||
|
||||
m_params.emplace_back( new PARAM<std::string>( "source.root_url", &m_Source.root_url, "" ) );
|
||||
|
||||
m_params.emplace_back( new PARAM<std::string>( "source.token", &m_Source.token, "" ) );
|
||||
}
|
||||
|
||||
|
||||
wxString HTTP_LIB_SETTINGS::getFileExt() const
|
||||
{
|
||||
return HTTPLibraryFileExtension;
|
||||
}
|
|
@ -152,6 +152,7 @@ const std::string HtmlFileExtension( "html" );
|
|||
const std::string EquFileExtension( "equ" );
|
||||
const std::string HotkeyFileExtension( "hotkeys" );
|
||||
const std::string DatabaseLibraryFileExtension( "kicad_dbl" );
|
||||
const std::string HTTPLibraryFileExtension( "kicad_httplib" );
|
||||
|
||||
const std::string ArchiveFileExtension( "zip" );
|
||||
|
||||
|
|
|
@ -395,6 +395,7 @@ set( EESCHEMA_SRCS
|
|||
sch_plugins/legacy/sch_legacy_plugin.cpp
|
||||
sch_plugins/legacy/sch_legacy_plugin_helpers.cpp
|
||||
sch_plugins/database/sch_database_plugin.cpp
|
||||
sch_plugins/http_lib/sch_http_lib_plugin.cpp
|
||||
|
||||
tools/assign_footprints.cpp
|
||||
tools/backannotate.cpp
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <sch_plugins/easyedapro/sch_easyedapro_plugin.h>
|
||||
#include <sch_plugins/database/sch_database_plugin.h>
|
||||
#include <sch_plugins/ltspice/ltspice_sch_plugin.h>
|
||||
#include <sch_plugins/http_lib/sch_http_lib_plugin.h>
|
||||
#include <common.h> // for ExpandEnvVarSubstitutions
|
||||
|
||||
#include <wildcards_and_files_ext.h>
|
||||
|
@ -72,6 +73,7 @@ SCH_PLUGIN* SCH_IO_MGR::FindPlugin( SCH_FILE_T aFileType )
|
|||
case SCH_EASYEDA: return new SCH_EASYEDA_PLUGIN();
|
||||
case SCH_EASYEDAPRO: return new SCH_EASYEDAPRO_PLUGIN();
|
||||
case SCH_LTSPICE: return new SCH_LTSPICE_PLUGIN();
|
||||
case SCH_HTTP: return new SCH_HTTP_LIB_PLUGIN();
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +106,7 @@ const wxString SCH_IO_MGR::ShowType( SCH_FILE_T aType )
|
|||
case SCH_EASYEDA: return wxString( wxT( "EasyEDA (JLCEDA) Std" ) );
|
||||
case SCH_EASYEDAPRO: return wxString( wxT( "EasyEDA (JLCEDA) Pro" ) );
|
||||
case SCH_LTSPICE: return wxString( wxT( "LTspice" ) );
|
||||
case SCH_HTTP: return wxString( wxT( "HTTP" ) );
|
||||
default: return wxString::Format( _( "Unknown SCH_FILE_T value: %d" ),
|
||||
aType );
|
||||
}
|
||||
|
@ -134,6 +137,8 @@ SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::EnumFromStr( const wxString& aType )
|
|||
return SCH_EASYEDAPRO;
|
||||
else if( aType == wxT( "LTspice" ) )
|
||||
return SCH_LTSPICE;
|
||||
else if( aType == wxT( "HTTP" ) )
|
||||
return SCH_HTTP;
|
||||
|
||||
// wxASSERT( blow up here )
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
SCH_EASYEDA, ///< EasyEDA Std schematic file
|
||||
SCH_EASYEDAPRO, ///< EasyEDA Pro archive
|
||||
SCH_LTSPICE, ///< LtSpice Schematic format
|
||||
SCH_HTTP, ///< KiCad HTTP library
|
||||
|
||||
// Add your schematic type here.
|
||||
SCH_FILE_UNKNOWN
|
||||
|
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Andre F. K. Iwers <iwers11@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <fmt.h>
|
||||
#include <lib_symbol.h>
|
||||
#include <symbol_lib_table.h>
|
||||
|
||||
#include <http_lib/http_lib_connection.h>
|
||||
#include "sch_http_lib_plugin.h"
|
||||
|
||||
|
||||
SCH_HTTP_LIB_PLUGIN::SCH_HTTP_LIB_PLUGIN() :
|
||||
m_libTable( nullptr )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SCH_HTTP_LIB_PLUGIN::~SCH_HTTP_LIB_PLUGIN()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
|
||||
const wxString& aLibraryPath,
|
||||
const STRING_UTF8_MAP* aProperties )
|
||||
{
|
||||
std::vector<LIB_SYMBOL*> symbols;
|
||||
EnumerateSymbolLib( symbols, aLibraryPath, aProperties );
|
||||
|
||||
for( LIB_SYMBOL* symbol : symbols )
|
||||
aSymbolNameList.Add( symbol->GetName() );
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
|
||||
const wxString& aLibraryPath,
|
||||
const STRING_UTF8_MAP* aProperties )
|
||||
{
|
||||
wxCHECK_RET( m_libTable, _("httplib plugin missing library table handle!") );
|
||||
ensureSettings( aLibraryPath );
|
||||
ensureConnection();
|
||||
|
||||
if( !m_conn)
|
||||
{
|
||||
THROW_IO_ERROR( m_lastError );
|
||||
return;
|
||||
}
|
||||
|
||||
bool powerSymbolsOnly =
|
||||
( aProperties
|
||||
&& aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
|
||||
|
||||
// clear buffer
|
||||
m_cachedParts.clear();
|
||||
|
||||
for(const HTTP_LIB_CATEGORY& category : m_conn->getCategories() )
|
||||
{
|
||||
std::vector<HTTP_LIB_PART> found_parts;
|
||||
|
||||
if( !m_conn->SelectAll( category, found_parts ) )
|
||||
{
|
||||
if( !m_conn->GetLastError().empty() )
|
||||
{
|
||||
wxString msg = wxString::Format( _( "Error retriving data from HTTP library %s: %s" ),
|
||||
category.name, m_conn->GetLastError() );
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// cache information for later use in LoadSymbol()
|
||||
m_cachedParts.emplace( category.id, found_parts );
|
||||
|
||||
for( const HTTP_LIB_PART& part : found_parts )
|
||||
{
|
||||
|
||||
wxString libIDString( part.name );
|
||||
|
||||
LIB_SYMBOL* symbol = loadSymbolFromPart( libIDString, category, part );
|
||||
|
||||
if( symbol && ( !powerSymbolsOnly || symbol->IsPower() ) )
|
||||
aSymbolList.emplace_back( symbol );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LIB_SYMBOL* SCH_HTTP_LIB_PLUGIN::LoadSymbol( const wxString& aLibraryPath,
|
||||
const wxString& aAliasName,
|
||||
const STRING_UTF8_MAP* aProperties )
|
||||
{
|
||||
wxCHECK( m_libTable, nullptr );
|
||||
ensureSettings( aLibraryPath );
|
||||
ensureConnection();
|
||||
|
||||
if( !m_conn )
|
||||
THROW_IO_ERROR( m_lastError );
|
||||
|
||||
std::string part_id = "";
|
||||
|
||||
std::string partName( aAliasName.ToUTF8() );
|
||||
|
||||
const HTTP_LIB_CATEGORY* foundCategory = nullptr;
|
||||
HTTP_LIB_PART result;
|
||||
|
||||
std::vector<HTTP_LIB_CATEGORY> categories = m_conn->getCategories();
|
||||
|
||||
std::tuple relations = m_conn->getCachedParts()[partName];
|
||||
|
||||
// get the matching category
|
||||
for( const HTTP_LIB_CATEGORY& categoryIter : categories )
|
||||
{
|
||||
std::string associatedCatID = std::get<1>( relations );
|
||||
if( categoryIter.id == associatedCatID )
|
||||
{
|
||||
foundCategory = &categoryIter;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return Null if no category was found. This should never happen
|
||||
if( foundCategory == NULL )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "loadSymbol: no category found for %s" ), partName );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the matching query ID
|
||||
for( const auto& part : m_cachedParts[foundCategory->id] )
|
||||
{
|
||||
if( part.id == std::get<0>( relations ) )
|
||||
{
|
||||
part_id = part.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_conn->SelectOne( part_id, result ) )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "loadSymbol: SelectOne (%s) found in %s" ), part_id,
|
||||
foundCategory->name );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "loadSymbol: SelectOne (%s) failed for category %s" ),
|
||||
part_id, foundCategory->name );
|
||||
|
||||
THROW_IO_ERROR( m_lastError );
|
||||
}
|
||||
|
||||
wxCHECK( foundCategory, nullptr );
|
||||
|
||||
return loadSymbolFromPart( aAliasName, *foundCategory, result );
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::GetSubLibraryNames( std::vector<wxString>& aNames )
|
||||
{
|
||||
ensureSettings( wxEmptyString );
|
||||
|
||||
aNames.clear();
|
||||
|
||||
std::set<wxString> categoryNames;
|
||||
|
||||
for( const HTTP_LIB_CATEGORY& categoryIter : m_conn->getCategories() )
|
||||
{
|
||||
if( categoryNames.count( categoryIter.name ) )
|
||||
continue;
|
||||
|
||||
aNames.emplace_back( categoryIter.name );
|
||||
categoryNames.insert( categoryIter.name );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::GetAvailableSymbolFields( std::vector<wxString>& aNames )
|
||||
{
|
||||
// TODO: Implement this sometime; This is currently broken...
|
||||
std::copy( m_customFields.begin(), m_customFields.end(), std::back_inserter( aNames ) );
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::GetDefaultSymbolFields( std::vector<wxString>& aNames )
|
||||
{
|
||||
std::copy( m_defaultShownFields.begin(), m_defaultShownFields.end(),
|
||||
std::back_inserter( aNames ) );
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::ensureSettings( const wxString& aSettingsPath )
|
||||
{
|
||||
|
||||
auto tryLoad = [&]()
|
||||
{
|
||||
if( !m_settings->LoadFromFile() )
|
||||
{
|
||||
wxString msg = wxString::Format( _( "HTTP library settings file %s missing or invalid" ),
|
||||
aSettingsPath );
|
||||
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
if( m_settings->m_Source.api_version.empty() )
|
||||
{
|
||||
wxString msg = wxString::Format(
|
||||
_( "HTTP library settings file %s is missing the API version number!" ), aSettingsPath );
|
||||
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
if( m_settings->getSupportedAPIVersion() != m_settings->m_Source.api_version )
|
||||
{
|
||||
wxString msg = wxString::Format(
|
||||
_( "HTTP library settings file %s indicates API version conflict (Settings file: %s <-> KiCad: %s)!" ),
|
||||
aSettingsPath,
|
||||
m_settings->m_Source.api_version,
|
||||
m_settings->getSupportedAPIVersion() );
|
||||
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
if( m_settings->m_Source.root_url.empty() )
|
||||
{
|
||||
wxString msg = wxString::Format(
|
||||
_( "HTTP library settings file %s is missing the root URL!" ),
|
||||
aSettingsPath );
|
||||
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
// map lib source type
|
||||
m_settings->m_Source.type = m_settings->get_HTTP_LIB_SOURCE_TYPE();
|
||||
if( m_settings->m_Source.type == HTTP_LIB_SOURCE_TYPE::INVALID )
|
||||
{
|
||||
wxString msg = wxString::Format(
|
||||
_( "HTTP library settings file has an invalid library type" ), aSettingsPath );
|
||||
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
// make sure that the root url finishes with a forward slash
|
||||
if( m_settings->m_Source.root_url.at( m_settings->m_Source.root_url.length() - 1 ) != '/' )
|
||||
{
|
||||
m_settings->m_Source.root_url += "/";
|
||||
}
|
||||
|
||||
// Append api version to root URL
|
||||
m_settings->m_Source.root_url += m_settings->m_Source.api_version + "/";
|
||||
|
||||
};
|
||||
|
||||
if( !m_settings && !aSettingsPath.IsEmpty() )
|
||||
{
|
||||
std::string path( aSettingsPath.ToUTF8() );
|
||||
m_settings = std::make_unique<HTTP_LIB_SETTINGS>( path );
|
||||
|
||||
m_settings->SetReadOnly( true );
|
||||
|
||||
tryLoad();
|
||||
}
|
||||
else if( m_settings )
|
||||
{
|
||||
// If we have valid settings but no connection yet; reload settings in case user is editing
|
||||
tryLoad();
|
||||
}
|
||||
else if( !m_settings )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "ensureSettings: no settings available!" ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::ensureConnection()
|
||||
{
|
||||
wxCHECK_RET( m_settings, "Call ensureSettings before ensureConnection!" );
|
||||
|
||||
connect();
|
||||
|
||||
if( !m_conn || !m_conn->IsValidEnpoint() )
|
||||
{
|
||||
wxString msg = wxString::Format(
|
||||
_( "Could not connect to %s. Errors: %s" ),
|
||||
m_settings->m_Source.root_url, m_lastError );
|
||||
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::connect()
|
||||
{
|
||||
wxCHECK_RET( m_settings, "Call ensureSettings before connect()!" );
|
||||
|
||||
if( !m_conn )
|
||||
{
|
||||
|
||||
m_conn = std::make_unique<HTTP_LIB_CONNECTION>( m_settings->m_Source, true );
|
||||
|
||||
if( !m_conn->IsValidEnpoint() )
|
||||
{
|
||||
m_lastError = m_conn->GetLastError();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
LIB_SYMBOL* SCH_HTTP_LIB_PLUGIN::loadSymbolFromPart( const wxString& aSymbolName,
|
||||
const HTTP_LIB_CATEGORY& aCategory,
|
||||
const HTTP_LIB_PART& aPart )
|
||||
{
|
||||
LIB_SYMBOL* symbol = nullptr;
|
||||
LIB_SYMBOL* originalSymbol = nullptr;
|
||||
LIB_ID symbolId;
|
||||
|
||||
std::string symbolIdStr = aPart.symbolIdStr;
|
||||
|
||||
// Get or Create the symbol using the found symbol
|
||||
if( !symbolIdStr.empty() )
|
||||
{
|
||||
symbolId.Parse( symbolIdStr );
|
||||
|
||||
if( symbolId.IsValid() )
|
||||
{
|
||||
originalSymbol = m_libTable->LoadSymbol( symbolId );
|
||||
}
|
||||
|
||||
if( originalSymbol )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: found original symbol '%s'" ),
|
||||
symbolIdStr );
|
||||
|
||||
symbol = originalSymbol->Duplicate();
|
||||
symbol->SetSourceLibId( symbolId );
|
||||
|
||||
symbol->LibId().SetSubLibraryName( aCategory.name );
|
||||
}
|
||||
else if( !symbolId.IsValid() )
|
||||
{
|
||||
wxLogTrace( traceHTTPLib,
|
||||
wxT( "loadSymbolFromPart: source symbol id '%s' is invalid, "
|
||||
"will create empty symbol" ),
|
||||
symbolIdStr );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( traceHTTPLib,
|
||||
wxT( "loadSymbolFromPart: source symbol '%s' not found, "
|
||||
"will create empty symbol" ),
|
||||
symbolIdStr );
|
||||
}
|
||||
}
|
||||
|
||||
if( !symbol )
|
||||
{
|
||||
// Actual symbol not found: return metadata only; error will be
|
||||
// indicated in the symbol chooser
|
||||
symbol = new LIB_SYMBOL( aSymbolName );
|
||||
symbol->LibId().SetSubLibraryName( aCategory.name );
|
||||
}
|
||||
|
||||
LIB_FIELD* field;
|
||||
|
||||
for( auto& _field : aPart.fields )
|
||||
{
|
||||
std::string fieldName = _field.first;
|
||||
std::tuple fieldProperties = _field.second;
|
||||
|
||||
if( fieldName == footprint_field )
|
||||
{
|
||||
field = &symbol->GetFootprintField();
|
||||
field->SetText( std::get<0>( fieldProperties ) );
|
||||
field->SetVisible( std::get<1>( fieldProperties ) );
|
||||
}
|
||||
else if( fieldName == description_field )
|
||||
{
|
||||
field = &symbol->GetDescriptionField();
|
||||
field->SetText( std::get<0>( fieldProperties ) );
|
||||
field->SetVisible( std::get<1>( fieldProperties ) );
|
||||
}
|
||||
else if( fieldName == value_field )
|
||||
{
|
||||
field = &symbol->GetValueField();
|
||||
field->SetText( std::get<0>( fieldProperties ) );
|
||||
field->SetVisible( std::get<1>( fieldProperties ) );
|
||||
}
|
||||
else if( fieldName == datasheet_field )
|
||||
{
|
||||
field = &symbol->GetDatasheetField();
|
||||
field->SetText( std::get<0>( fieldProperties ) );
|
||||
field->SetVisible( std::get<1>( fieldProperties ) );
|
||||
}
|
||||
else if( fieldName == reference_field )
|
||||
{
|
||||
field = &symbol->GetReferenceField();
|
||||
field->SetText( std::get<0>( fieldProperties ) );
|
||||
field->SetVisible( std::get<1>( fieldProperties ) );
|
||||
}
|
||||
else if( fieldName == keywords_field )
|
||||
{
|
||||
symbol->SetKeyWords( std::get<0>( fieldProperties ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generic fields
|
||||
field = new LIB_FIELD( symbol->GetNextAvailableFieldId() );
|
||||
field->SetName( fieldName );
|
||||
|
||||
field->SetText( std::get<0>( fieldProperties ) );
|
||||
field->SetVisible( std::get<1>( fieldProperties ) );
|
||||
symbol->AddField( field );
|
||||
|
||||
m_customFields.insert( fieldName );
|
||||
}
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
void SCH_HTTP_LIB_PLUGIN::SaveSymbol( const wxString& aLibraryPath, const LIB_SYMBOL* aSymbol,
|
||||
const STRING_UTF8_MAP* aProperties )
|
||||
{
|
||||
// TODO: Implement this sometime;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Andre F. K. Iwers <iwers11@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef KICAD_SCH_HTTP_LIB_PLUGIN_H
|
||||
#define KICAD_SCH_HTTP_LIB_PLUGIN_H
|
||||
|
||||
#include "http_lib/http_lib_settings.h"
|
||||
#include <http_lib/http_lib_connection.h>
|
||||
|
||||
#include <sch_io_mgr.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
|
||||
/**
|
||||
* A KiCad HTTP library provides both symbol and footprint metadata, so there are "shim" plugins
|
||||
* on both the symbol and footprint side of things that expose the database contents to the
|
||||
* schematic and board editors. The architecture of these is slightly different from the other
|
||||
* plugins because the backing file is just a configuration file rather than something that
|
||||
* contains symbol or footprint data.
|
||||
*/
|
||||
class SCH_HTTP_LIB_PLUGIN : public SCH_PLUGIN
|
||||
{
|
||||
public:
|
||||
|
||||
SCH_HTTP_LIB_PLUGIN();
|
||||
virtual ~SCH_HTTP_LIB_PLUGIN();
|
||||
|
||||
const wxString GetName() const override
|
||||
{
|
||||
return wxT( "HTTP library" );
|
||||
}
|
||||
|
||||
const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
|
||||
{
|
||||
return PLUGIN_FILE_DESC( _HKI( "KiCad HTTP library files" ),
|
||||
{ HTTPLibraryFileExtension } );
|
||||
}
|
||||
|
||||
int GetModifyHash() const override { return 0; }
|
||||
|
||||
void EnumerateSymbolLib( wxArrayString& aSymbolNameList,
|
||||
const wxString& aLibraryPath,
|
||||
const STRING_UTF8_MAP* aProperties = nullptr ) override;
|
||||
|
||||
void EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
|
||||
const wxString& aLibraryPath,
|
||||
const STRING_UTF8_MAP* aProperties = nullptr ) override;
|
||||
|
||||
LIB_SYMBOL* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
|
||||
const STRING_UTF8_MAP* aProperties = nullptr ) override;
|
||||
|
||||
bool SupportsSubLibraries() const override { return true; }
|
||||
|
||||
void GetSubLibraryNames( std::vector<wxString>& aNames ) override;
|
||||
|
||||
void GetAvailableSymbolFields( std::vector<wxString>& aNames ) override;
|
||||
|
||||
void GetDefaultSymbolFields( std::vector<wxString>& aNames ) override;
|
||||
|
||||
bool IsSymbolLibWritable( const wxString& aLibraryPath ) override
|
||||
{
|
||||
// TODO: HTTP libraries are well capabale of supporting this.
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetLibTable( SYMBOL_LIB_TABLE* aTable ) override
|
||||
{
|
||||
m_libTable = aTable;
|
||||
}
|
||||
|
||||
|
||||
HTTP_LIB_SETTINGS* Settings() const { return m_settings.get(); }
|
||||
|
||||
void SaveSymbol( const wxString& aLibraryPath, const LIB_SYMBOL* aSymbol,
|
||||
const STRING_UTF8_MAP* aProperties = nullptr ) override;
|
||||
|
||||
private:
|
||||
|
||||
void ensureSettings( const wxString& aSettingsPath );
|
||||
|
||||
void ensureConnection();
|
||||
|
||||
void connect();
|
||||
|
||||
LIB_SYMBOL* loadSymbolFromPart( const wxString& aSymbolName,
|
||||
const HTTP_LIB_CATEGORY& aCategory,
|
||||
const HTTP_LIB_PART& aPart );
|
||||
|
||||
SYMBOL_LIB_TABLE* m_libTable;
|
||||
|
||||
std::map<std::string, std::vector<HTTP_LIB_PART>> m_cachedParts;
|
||||
|
||||
/// Generally will be null if no valid connection is established
|
||||
std::unique_ptr<HTTP_LIB_CONNECTION> m_conn;
|
||||
|
||||
std::unique_ptr<HTTP_LIB_SETTINGS> m_settings;
|
||||
|
||||
std::set<wxString> m_customFields;
|
||||
|
||||
std::set<wxString> m_defaultShownFields;
|
||||
|
||||
wxString m_lastError;
|
||||
|
||||
std::string symbol_field = "symbol";
|
||||
std::string footprint_field = "footprint";
|
||||
std::string description_field = "description";
|
||||
std::string keywords_field = "keywords";
|
||||
std::string value_field = "value";
|
||||
std::string datasheet_field = "datasheet";
|
||||
std::string reference_field = "reference";
|
||||
|
||||
};
|
||||
|
||||
#endif //KICAD_SCH_HTTP_LIB_PLUGIN_H
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Andre F. K. Iwers <iwers11@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KICAD_HTTP_LIB_CONNECTION_H
|
||||
#define KICAD_HTTP_LIB_CONNECTION_H
|
||||
|
||||
#include <any>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "http_lib/http_lib_settings.h"
|
||||
#include <kicad_curl/kicad_curl_easy.h>
|
||||
|
||||
extern const char* const traceHTTPLib;
|
||||
|
||||
|
||||
class HTTP_LIB_CONNECTION
|
||||
{
|
||||
public:
|
||||
static const long DEFAULT_TIMEOUT = 10;
|
||||
|
||||
HTTP_LIB_CONNECTION( const HTTP_LIB_SOURCE& aSource, bool aTestConnectionNow );
|
||||
|
||||
~HTTP_LIB_CONNECTION();
|
||||
|
||||
bool IsValidEnpoint() const;
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a single part with full details from the HTTP library.
|
||||
* @param aPk is the primary key of the part
|
||||
* @param aResult will conatain the part if one was found
|
||||
* @return true if aResult was filled; false otherwise
|
||||
*/
|
||||
bool SelectOne( const std::string aPartID, HTTP_LIB_PART& aFetchedPart );
|
||||
|
||||
/**
|
||||
* Retrieves all parts from a specific category from the HTTP library.
|
||||
* @param aPk is the primary key of the category
|
||||
* @param aResults will be filled with all parts in that category
|
||||
* @return true if the query succeeded and at least one part was found, false otherwise
|
||||
*/
|
||||
bool SelectAll( const HTTP_LIB_CATEGORY& aCategory, std::vector<HTTP_LIB_PART>& aParts );
|
||||
|
||||
std::string GetLastError() const { return m_lastError; }
|
||||
|
||||
std::vector<HTTP_LIB_CATEGORY> getCategories() const { return m_categories; }
|
||||
|
||||
auto getCachedParts() { return m_cache; }
|
||||
|
||||
private:
|
||||
|
||||
// This is clunky but at the moment the only way to free the pointer after use without KiCad crashing.
|
||||
// at this point we can't use smart pointers as there is a problem with the order of how things are deleted/freed
|
||||
std::unique_ptr<KICAD_CURL_EASY> createCurlEasyObject()
|
||||
{
|
||||
|
||||
std::unique_ptr<KICAD_CURL_EASY> aCurl( new KICAD_CURL_EASY() );
|
||||
|
||||
//KICAD_CURL_EASY* aCurl = new KICAD_CURL_EASY();
|
||||
|
||||
// prepare curl
|
||||
aCurl->SetHeader( "Accept", "application/json" );
|
||||
aCurl->SetHeader( "Authorization", "Token " + m_token );
|
||||
|
||||
return aCurl;
|
||||
}
|
||||
|
||||
bool ValidateHTTPLibraryEndpoints();
|
||||
|
||||
bool syncCategories();
|
||||
|
||||
bool checkServerResponse( std::unique_ptr<KICAD_CURL_EASY>& m_curl );
|
||||
|
||||
bool boolFromString( const std::any& aVal );
|
||||
|
||||
wxString httpErrorCodeDescription( uint16_t http_code );
|
||||
|
||||
std::string m_token;
|
||||
std::string m_rootURL;
|
||||
std::string m_user_name;
|
||||
std::string m_user_pass;
|
||||
|
||||
HTTP_LIB_SOURCE_TYPE m_sourceType;
|
||||
|
||||
HTTP_LIB_PART m_cached_part;
|
||||
|
||||
// part.name part.id category.id
|
||||
std::map<std::string, std::tuple<std::string, std::string>> m_cache;
|
||||
|
||||
bool m_enpointValid = false;
|
||||
|
||||
std::string m_lastError;
|
||||
|
||||
std::vector<HTTP_LIB_CATEGORY> m_categories;
|
||||
|
||||
std::map<std::string, std::string> m_parts;
|
||||
|
||||
const std::string http_endpoint_categories = "categories";
|
||||
const std::string http_endpoint_parts = "parts";
|
||||
const std::string http_endpoint_settings = "settings";
|
||||
const std::string http_endpoint_auth = "authentication";
|
||||
|
||||
};
|
||||
|
||||
#endif //KICAD_HTTP_LIB_CONNECTION_H
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Andre F. K. Iwers <iwers11@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KICAD_HTTP_LIB_SETTINGS_H
|
||||
#define KICAD_HTTP_LIB_SETTINGS_H
|
||||
|
||||
#include <settings/json_settings.h>
|
||||
|
||||
|
||||
enum class HTTP_LIB_SOURCE_TYPE
|
||||
{
|
||||
REST_API,
|
||||
INVALID
|
||||
};
|
||||
|
||||
|
||||
struct HTTP_LIB_SOURCE
|
||||
{
|
||||
HTTP_LIB_SOURCE_TYPE type;
|
||||
std::string root_url;
|
||||
std::string api_version;
|
||||
std::string token;
|
||||
};
|
||||
|
||||
|
||||
struct HTTP_LIB_CATEGORY
|
||||
{
|
||||
std::string id; ///< id of category
|
||||
std::string name; ///< name of category
|
||||
};
|
||||
|
||||
struct HTTP_LIB_PART
|
||||
{
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string symbolIdStr;
|
||||
|
||||
std::map<std::string, std::tuple<std::string, bool>> fields; ///< additional generic fields
|
||||
};
|
||||
|
||||
class HTTP_LIB_SETTINGS : public JSON_SETTINGS
|
||||
{
|
||||
public:
|
||||
HTTP_LIB_SETTINGS( const std::string& aFilename );
|
||||
|
||||
virtual ~HTTP_LIB_SETTINGS() {}
|
||||
|
||||
HTTP_LIB_SOURCE m_Source;
|
||||
|
||||
HTTP_LIB_SOURCE_TYPE get_HTTP_LIB_SOURCE_TYPE()
|
||||
{
|
||||
if( sourceType.compare( "REST_API" ) == 0 )
|
||||
{
|
||||
return HTTP_LIB_SOURCE_TYPE::REST_API;
|
||||
}
|
||||
|
||||
return HTTP_LIB_SOURCE_TYPE::INVALID;
|
||||
}
|
||||
|
||||
std::string getSupportedAPIVersion() { return api_version; }
|
||||
|
||||
protected:
|
||||
wxString getFileExt() const override;
|
||||
|
||||
private:
|
||||
std::string sourceType;
|
||||
std::string api_version = "v1";
|
||||
};
|
||||
|
||||
#endif //KICAD_HTTP_LIB_SETTINGS_H
|
|
@ -136,6 +136,7 @@ extern const std::string HtmlFileExtension;
|
|||
extern const std::string EquFileExtension;
|
||||
extern const std::string HotkeyFileExtension;
|
||||
extern const std::string DatabaseLibraryFileExtension;
|
||||
extern const std::string HTTPLibraryFileExtension;
|
||||
|
||||
extern const std::string ArchiveFileExtension;
|
||||
|
||||
|
|
Loading…
Reference in New Issue