465 lines
14 KiB
C++
465 lines
14 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* 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-3.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
|
|
*/
|
|
|
|
|
|
/**
|
|
* @file pcbnew/cross-probing.cpp
|
|
* @brief Cross probing functions to handle communication to andfrom Eeschema.
|
|
*/
|
|
|
|
/**
|
|
* Handle messages between Pcbnew and Eeschema via a socket, the port numbers are
|
|
* KICAD_PCB_PORT_SERVICE_NUMBER (currently 4242) (Eeschema to Pcbnew)
|
|
* KICAD_SCH_PORT_SERVICE_NUMBER (currently 4243) (Pcbnew to Eeschema)
|
|
* Note: these ports must be enabled for firewall protection
|
|
*/
|
|
|
|
#include <fctsys.h>
|
|
#include <pgm_base.h>
|
|
#include <kiface_i.h>
|
|
#include <kiway_express.h>
|
|
#include <pcb_edit_frame.h>
|
|
#include <eda_dde.h>
|
|
#include <macros.h>
|
|
|
|
#include <pcbnew_id.h>
|
|
#include <class_board.h>
|
|
#include <class_module.h>
|
|
#include <class_track.h>
|
|
#include <class_zone.h>
|
|
|
|
#include <collectors.h>
|
|
#include <pcbnew.h>
|
|
#include <board_netlist_updater.h>
|
|
#include <netlist_reader.h>
|
|
#include <pcb_netlist.h>
|
|
#include <dialogs/dialog_update_pcb.h>
|
|
|
|
#include <tools/pcb_actions.h>
|
|
#include <tool/tool_manager.h>
|
|
#include <tools/selection_tool.h>
|
|
#include <pcb_draw_panel_gal.h>
|
|
#include <pcb_painter.h>
|
|
|
|
/* Execute a remote command send by Eeschema via a socket,
|
|
* port KICAD_PCB_PORT_SERVICE_NUMBER
|
|
* cmdline = received command from Eeschema
|
|
* Commands are
|
|
* $PART: "reference" put cursor on component
|
|
* $PIN: "pin name" $PART: "reference" put cursor on the footprint pin
|
|
* $NET: "net name" highlight the given net (if highlight tool is active)
|
|
* They are a keyword followed by a quoted string.
|
|
*/
|
|
void PCB_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
|
|
{
|
|
char line[1024];
|
|
wxString msg;
|
|
wxString modName;
|
|
char* idcmd;
|
|
char* text;
|
|
MODULE* module = NULL;
|
|
D_PAD* pad = NULL;
|
|
BOARD* pcb = GetBoard();
|
|
wxPoint pos;
|
|
|
|
strncpy( line, cmdline, sizeof(line) - 1 );
|
|
line[sizeof(line) - 1] = 0;
|
|
|
|
idcmd = strtok( line, " \n\r" );
|
|
text = strtok( NULL, "\"\n\r" );
|
|
|
|
if( idcmd == NULL )
|
|
return;
|
|
|
|
if( strcmp( idcmd, "$NET:" ) == 0 )
|
|
{
|
|
if( GetToolId() == ID_PCB_HIGHLIGHT_BUTT )
|
|
{
|
|
wxString net_name = FROM_UTF8( text );
|
|
NETINFO_ITEM* netinfo = pcb->FindNet( net_name );
|
|
int netcode = 0;
|
|
|
|
if( netinfo )
|
|
netcode = netinfo->GetNet();
|
|
|
|
if( IsGalCanvasActive() )
|
|
{
|
|
auto view = m_toolManager->GetView();
|
|
auto rs = view->GetPainter()->GetSettings();
|
|
rs->SetHighlight( true, netcode );
|
|
view->UpdateAllLayersColor();
|
|
|
|
BOX2I bbox;
|
|
bool first = true;
|
|
|
|
auto merge_area = [netcode, &bbox, &first]( BOARD_CONNECTED_ITEM* aItem )
|
|
{
|
|
if( aItem->GetNetCode() == netcode )
|
|
{
|
|
if( first )
|
|
{
|
|
bbox = aItem->GetBoundingBox();
|
|
first = false;
|
|
}
|
|
else
|
|
{
|
|
bbox.Merge( aItem->GetBoundingBox() );
|
|
}
|
|
}
|
|
};
|
|
|
|
for( auto zone : pcb->Zones() )
|
|
merge_area( zone );
|
|
|
|
for( auto track : pcb->Tracks() )
|
|
merge_area( track );
|
|
|
|
for( auto mod : pcb->Modules() )
|
|
for ( auto mod_pad : mod->Pads() )
|
|
merge_area( mod_pad );
|
|
|
|
if( netcode > 0 && bbox.GetWidth() > 0 && bbox.GetHeight() > 0 )
|
|
{
|
|
auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
|
|
auto screenSize = view->ToWorld( GetGalCanvas()->GetClientSize(), false );
|
|
double ratio = std::max( fabs( bbSize.x / screenSize.x ),
|
|
fabs( bbSize.y / screenSize.y ) );
|
|
double scale = view->GetScale() / ratio;
|
|
|
|
view->SetScale( scale );
|
|
view->SetCenter( bbox.Centre() );
|
|
}
|
|
|
|
GetGalCanvas()->Refresh();
|
|
}
|
|
else
|
|
{
|
|
if( netcode > 0 )
|
|
{
|
|
pcb->HighLightON();
|
|
pcb->SetHighLightNet( netcode );
|
|
}
|
|
else
|
|
{
|
|
pcb->HighLightOFF();
|
|
pcb->SetHighLightNet( -1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if( text == NULL )
|
|
return;
|
|
|
|
if( strcmp( idcmd, "$PART:" ) == 0 )
|
|
{
|
|
modName = FROM_UTF8( text );
|
|
|
|
module = pcb->FindModuleByReference( modName );
|
|
|
|
if( module )
|
|
msg.Printf( _( "%s found" ), modName );
|
|
else
|
|
msg.Printf( _( "%s not found" ), modName );
|
|
|
|
SetStatusText( msg );
|
|
|
|
if( module )
|
|
pos = module->GetPosition();
|
|
}
|
|
else if( strcmp( idcmd, "$SHEET:" ) == 0 )
|
|
{
|
|
msg.Printf( _( "Selecting all from sheet \"%s\"" ), FROM_UTF8( text ) );
|
|
wxString sheetStamp( FROM_UTF8( text ) );
|
|
SetStatusText( msg );
|
|
GetToolManager()->RunAction( PCB_ACTIONS::selectOnSheetFromEeschema, true,
|
|
static_cast<void*>( &sheetStamp ) );
|
|
return;
|
|
}
|
|
else if( strcmp( idcmd, "$PIN:" ) == 0 )
|
|
{
|
|
wxString pinName;
|
|
int netcode = -1;
|
|
|
|
pinName = FROM_UTF8( text );
|
|
|
|
text = strtok( NULL, " \n\r" );
|
|
|
|
if( text && strcmp( text, "$PART:" ) == 0 )
|
|
text = strtok( NULL, "\"\n\r" );
|
|
|
|
modName = FROM_UTF8( text );
|
|
|
|
module = pcb->FindModuleByReference( modName );
|
|
|
|
if( module )
|
|
pad = module->FindPadByName( pinName );
|
|
|
|
if( pad )
|
|
{
|
|
netcode = pad->GetNetCode();
|
|
|
|
// put cursor on the pad:
|
|
pos = pad->GetPosition();
|
|
}
|
|
|
|
if( netcode > 0 ) // highlight the pad net
|
|
{
|
|
pcb->HighLightON();
|
|
pcb->SetHighLightNet( netcode );
|
|
}
|
|
else
|
|
{
|
|
pcb->HighLightOFF();
|
|
pcb->SetHighLightNet( -1 );
|
|
}
|
|
|
|
if( module == NULL )
|
|
{
|
|
msg.Printf( _( "%s not found" ), modName );
|
|
}
|
|
else if( pad == NULL )
|
|
{
|
|
msg.Printf( _( "%s pin %s not found" ), modName, pinName );
|
|
SetCurItem( module );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "%s pin %s found" ), modName, pinName );
|
|
SetCurItem( pad );
|
|
}
|
|
|
|
SetStatusText( msg );
|
|
}
|
|
|
|
if( module ) // if found, center the module on screen, and redraw the screen.
|
|
{
|
|
if( IsGalCanvasActive() )
|
|
{
|
|
GetToolManager()->RunAction( PCB_ACTIONS::crossProbeSchToPcb,
|
|
true,
|
|
pad ?
|
|
static_cast<BOARD_ITEM*>( pad ) :
|
|
static_cast<BOARD_ITEM*>( module )
|
|
);
|
|
}
|
|
else
|
|
{
|
|
SetCrossHairPosition( pos );
|
|
RedrawScreen( pos, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
std::string FormatProbeItem( BOARD_ITEM* aItem )
|
|
{
|
|
MODULE* module;
|
|
|
|
if( !aItem )
|
|
return "$CLEAR: \"HIGHLIGHTED\""; // message to clear highlight state
|
|
|
|
switch( aItem->Type() )
|
|
{
|
|
case PCB_MODULE_T:
|
|
module = (MODULE*) aItem;
|
|
return StrPrintf( "$PART: \"%s\"", TO_UTF8( module->GetReference() ) );
|
|
|
|
case PCB_PAD_T:
|
|
{
|
|
module = (MODULE*) aItem->GetParent();
|
|
wxString pad = ((D_PAD*)aItem)->GetName();
|
|
|
|
return StrPrintf( "$PART: \"%s\" $PAD: \"%s\"",
|
|
TO_UTF8( module->GetReference() ),
|
|
TO_UTF8( pad ) );
|
|
}
|
|
|
|
case PCB_MODULE_TEXT_T:
|
|
{
|
|
module = static_cast<MODULE*>( aItem->GetParent() );
|
|
|
|
TEXTE_MODULE* text_mod = static_cast<TEXTE_MODULE*>( aItem );
|
|
|
|
const char* text_key;
|
|
|
|
/* This can't be a switch since the break need to pull out
|
|
* from the outer switch! */
|
|
if( text_mod->GetType() == TEXTE_MODULE::TEXT_is_REFERENCE )
|
|
text_key = "$REF:";
|
|
else if( text_mod->GetType() == TEXTE_MODULE::TEXT_is_VALUE )
|
|
text_key = "$VAL:";
|
|
else
|
|
break;
|
|
|
|
return StrPrintf( "$PART: \"%s\" %s \"%s\"",
|
|
TO_UTF8( module->GetReference() ),
|
|
text_key,
|
|
TO_UTF8( text_mod->GetText() ) );
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
|
|
/* Send a remote command to Eeschema via a socket,
|
|
* aSyncItem = item to be located on schematic (module, pin or text)
|
|
* Commands are
|
|
* $PART: "reference" put cursor on component anchor
|
|
* $PART: "reference" $PAD: "pad number" put cursor on the component pin
|
|
* $PART: "reference" $REF: "reference" put cursor on the component ref
|
|
* $PART: "reference" $VAL: "value" put cursor on the component value
|
|
*/
|
|
void PCB_EDIT_FRAME::SendMessageToEESCHEMA( BOARD_ITEM* aSyncItem )
|
|
{
|
|
std::string packet = FormatProbeItem( aSyncItem );
|
|
|
|
if( packet.size() )
|
|
{
|
|
if( Kiface().IsSingle() )
|
|
SendCommand( MSG_TO_SCH, packet.c_str() );
|
|
else
|
|
{
|
|
// Typically ExpressMail is going to be s-expression packets, but since
|
|
// we have existing interpreter of the cross probe packet on the other
|
|
// side in place, we use that here.
|
|
Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void PCB_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
|
|
{
|
|
std::string packet = StrPrintf( "$NET: \"%s\"", TO_UTF8( aNetName ) );
|
|
|
|
if( packet.size() )
|
|
{
|
|
if( Kiface().IsSingle() )
|
|
SendCommand( MSG_TO_SCH, packet.c_str() );
|
|
else
|
|
{
|
|
// Typically ExpressMail is going to be s-expression packets, but since
|
|
// we have existing interpreter of the cross probe packet on the other
|
|
// side in place, we use that here.
|
|
Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
|
{
|
|
const std::string& payload = mail.GetPayload();
|
|
|
|
switch( mail.Command() )
|
|
{
|
|
case MAIL_CROSS_PROBE:
|
|
ExecuteRemoteCommand( payload.c_str() );
|
|
break;
|
|
|
|
case MAIL_SCH_PCB_UPDATE:
|
|
{
|
|
NETLIST netlist;
|
|
size_t split = payload.find( '\n' );
|
|
wxCHECK( split != std::string::npos, /*void*/ );
|
|
|
|
// Extract options and netlist
|
|
std::string options = payload.substr( 0, split );
|
|
std::string netlistData = payload.substr( split + 1 );
|
|
|
|
// Quiet update options
|
|
bool by_reference = options.find( "by-reference" ) != std::string::npos;
|
|
bool by_timestamp = options.find( "by-timestamp" ) != std::string::npos;
|
|
wxASSERT( !( by_reference && by_timestamp ) ); // only one at a time please
|
|
|
|
try
|
|
{
|
|
STRING_LINE_READER* lineReader = new STRING_LINE_READER( netlistData, _( "Eeschema netlist" ) );
|
|
KICAD_NETLIST_READER netlistReader( lineReader, &netlist );
|
|
netlistReader.LoadNetlist();
|
|
}
|
|
catch( const IO_ERROR& )
|
|
{
|
|
assert( false ); // should never happen
|
|
}
|
|
|
|
if( by_reference || by_timestamp )
|
|
{
|
|
netlist.SetDeleteExtraFootprints( false );
|
|
netlist.SetFindByTimeStamp( by_timestamp );
|
|
netlist.SetReplaceFootprints( true );
|
|
|
|
BOARD_NETLIST_UPDATER updater( this, GetBoard() );
|
|
updater.SetLookupByTimestamp( by_timestamp );
|
|
updater.SetDeleteUnusedComponents( false );
|
|
updater.SetReplaceFootprints( true );
|
|
updater.SetDeleteSinglePadNets( false );
|
|
updater.UpdateNetlist( netlist );
|
|
}
|
|
else
|
|
{
|
|
DIALOG_UPDATE_PCB updateDialog( this, &netlist );
|
|
updateDialog.ShowModal();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MAIL_IMPORT_FILE:
|
|
{
|
|
// Extract file format type and path (plugin type and path separated with \n)
|
|
size_t split = payload.find( '\n' );
|
|
wxCHECK( split != std::string::npos, /*void*/ );
|
|
int importFormat;
|
|
|
|
try
|
|
{
|
|
importFormat = std::stoi( payload.substr( 0, split ) );
|
|
}
|
|
catch( std::invalid_argument& )
|
|
{
|
|
wxFAIL;
|
|
importFormat = -1;
|
|
}
|
|
|
|
std::string path = payload.substr( split + 1 );
|
|
wxASSERT( !path.empty() );
|
|
|
|
if( importFormat >= 0 )
|
|
importFile( path, importFormat );
|
|
}
|
|
|
|
// many many others.
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|