Add PADS netlist format export
This commit is contained in:
parent
c5a02fc266
commit
cb01bca1f4
|
@ -145,6 +145,7 @@ const std::string FILEEXT::CadstarNetlistFileExtension( "frp" );
|
||||||
const std::string FILEEXT::OrCadPcb2NetlistFileExtension( "net" );
|
const std::string FILEEXT::OrCadPcb2NetlistFileExtension( "net" );
|
||||||
const std::string FILEEXT::NetlistFileExtension( "net" );
|
const std::string FILEEXT::NetlistFileExtension( "net" );
|
||||||
const std::string FILEEXT::AllegroNetlistFileExtension( "txt" );
|
const std::string FILEEXT::AllegroNetlistFileExtension( "txt" );
|
||||||
|
const std::string FILEEXT::PADSNetlistFileExtension( "asc" );
|
||||||
const std::string FILEEXT::FootprintAssignmentFileExtension( "cmp" );
|
const std::string FILEEXT::FootprintAssignmentFileExtension( "cmp" );
|
||||||
const std::string FILEEXT::GerberFileExtension( "gbr" );
|
const std::string FILEEXT::GerberFileExtension( "gbr" );
|
||||||
const std::string FILEEXT::GerberJobFileExtension( "gbrjob" );
|
const std::string FILEEXT::GerberJobFileExtension( "gbrjob" );
|
||||||
|
@ -293,6 +294,12 @@ wxString FILEEXT::AllegroNetlistFileWildcard()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wxString FILEEXT::PADSNetlistFileWildcard()
|
||||||
|
{
|
||||||
|
return _( "PADS netlist files" ) + AddFileExtListToFilter( { PADSNetlistFileExtension } );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString FILEEXT::EasyEdaArchiveWildcard()
|
wxString FILEEXT::EasyEdaArchiveWildcard()
|
||||||
{
|
{
|
||||||
return _( "EasyEDA (JLCEDA) Std backup archive" ) + AddFileExtListToFilter( { "zip" } );
|
return _( "EasyEDA (JLCEDA) Std backup archive" ) + AddFileExtListToFilter( { "zip" } );
|
||||||
|
|
|
@ -424,6 +424,7 @@ set( EESCHEMA_SRCS
|
||||||
netlist_exporters/netlist_exporter_base.cpp
|
netlist_exporters/netlist_exporter_base.cpp
|
||||||
netlist_exporters/netlist_exporter_cadstar.cpp
|
netlist_exporters/netlist_exporter_cadstar.cpp
|
||||||
netlist_exporters/netlist_exporter_kicad.cpp
|
netlist_exporters/netlist_exporter_kicad.cpp
|
||||||
|
netlist_exporters/netlist_exporter_pads.cpp
|
||||||
netlist_exporters/netlist_exporter_orcadpcb2.cpp
|
netlist_exporters/netlist_exporter_orcadpcb2.cpp
|
||||||
netlist_exporters/netlist_exporter_spice.cpp
|
netlist_exporters/netlist_exporter_spice.cpp
|
||||||
netlist_exporters/netlist_exporter_spice_model.cpp
|
netlist_exporters/netlist_exporter_spice_model.cpp
|
||||||
|
|
|
@ -70,6 +70,7 @@ enum PANEL_NETLIST_INDEX
|
||||||
PANELORCADPCB2, /* Handle Netlist format OracdPcb2 */
|
PANELORCADPCB2, /* Handle Netlist format OracdPcb2 */
|
||||||
PANELALLEGRO, /* Handle Netlist format Allegro */
|
PANELALLEGRO, /* Handle Netlist format Allegro */
|
||||||
PANELCADSTAR, /* Handle Netlist format CadStar */
|
PANELCADSTAR, /* Handle Netlist format CadStar */
|
||||||
|
PANELPADS, /* Handle Netlist format PADS */
|
||||||
PANELSPICE, /* Handle Netlist format Spice */
|
PANELSPICE, /* Handle Netlist format Spice */
|
||||||
PANELSPICEMODEL, /* Handle Netlist format Spice Model (subcircuit) */
|
PANELSPICEMODEL, /* Handle Netlist format Spice Model (subcircuit) */
|
||||||
PANELCUSTOMBASE /* First auxiliary panel (custom netlists).
|
PANELCUSTOMBASE /* First auxiliary panel (custom netlists).
|
||||||
|
@ -264,6 +265,9 @@ DIALOG_EXPORT_NETLIST::DIALOG_EXPORT_NETLIST( SCH_EDIT_FRAME* parent ) :
|
||||||
m_PanelNetType[PANELALLEGRO] =
|
m_PanelNetType[PANELALLEGRO] =
|
||||||
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "Allegro" ), NET_TYPE_ALLEGRO, false );
|
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "Allegro" ), NET_TYPE_ALLEGRO, false );
|
||||||
|
|
||||||
|
m_PanelNetType[PANELPADS] =
|
||||||
|
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "PADS" ), NET_TYPE_PADS, false );
|
||||||
|
|
||||||
m_PanelNetType[PANELCADSTAR] =
|
m_PanelNetType[PANELCADSTAR] =
|
||||||
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "CadStar" ), NET_TYPE_CADSTAR, false );
|
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "CadStar" ), NET_TYPE_CADSTAR, false );
|
||||||
|
|
||||||
|
@ -512,6 +516,9 @@ bool DIALOG_EXPORT_NETLIST::TransferDataFromWindow()
|
||||||
case NET_TYPE_ALLEGRO:
|
case NET_TYPE_ALLEGRO:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NET_TYPE_PADS:
|
||||||
|
break;
|
||||||
|
|
||||||
default: // custom, NET_TYPE_CUSTOM1 and greater
|
default: // custom, NET_TYPE_CUSTOM1 and greater
|
||||||
{
|
{
|
||||||
title.Printf( _( "%s Export" ), currPage->m_TitleStringCtrl->GetValue() );
|
title.Printf( _( "%s Export" ), currPage->m_TitleStringCtrl->GetValue() );
|
||||||
|
@ -671,6 +678,12 @@ bool DIALOG_EXPORT_NETLIST::FilenamePrms( NETLIST_TYPE_ID aType, wxString * aExt
|
||||||
fileWildcard = FILEEXT::AllegroNetlistFileWildcard();
|
fileWildcard = FILEEXT::AllegroNetlistFileWildcard();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NET_TYPE_PADS:
|
||||||
|
fileExt = FILEEXT::PADSNetlistFileExtension;
|
||||||
|
fileWildcard = FILEEXT::PADSNetlistFileWildcard();
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
default: // custom, NET_TYPE_CUSTOM1 and greater
|
default: // custom, NET_TYPE_CUSTOM1 and greater
|
||||||
fileWildcard = FILEEXT::AllFilesWildcard();
|
fileWildcard = FILEEXT::AllFilesWildcard();
|
||||||
ret = false;
|
ret = false;
|
||||||
|
|
|
@ -41,6 +41,7 @@ enum NETLIST_TYPE_ID {
|
||||||
NET_TYPE_SPICE,
|
NET_TYPE_SPICE,
|
||||||
NET_TYPE_SPICE_MODEL,
|
NET_TYPE_SPICE_MODEL,
|
||||||
NET_TYPE_ALLEGRO,
|
NET_TYPE_ALLEGRO,
|
||||||
|
NET_TYPE_PADS,
|
||||||
NET_TYPE_CUSTOM1, /* NET_TYPE_CUSTOM1
|
NET_TYPE_CUSTOM1, /* NET_TYPE_CUSTOM1
|
||||||
* is the first id for user netlist format
|
* is the first id for user netlist format
|
||||||
* NET_TYPE_CUSTOM1+CUSTOMPANEL_COUNTMAX-1
|
* NET_TYPE_CUSTOM1+CUSTOMPANEL_COUNTMAX-1
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 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-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 <build_version.h>
|
||||||
|
#include <confirm.h>
|
||||||
|
|
||||||
|
#include <connection_graph.h>
|
||||||
|
#include <string_utils.h>
|
||||||
|
#include <sch_edit_frame.h>
|
||||||
|
#include <sch_reference_list.h>
|
||||||
|
|
||||||
|
#include "netlist_exporter_pads.h"
|
||||||
|
|
||||||
|
bool NETLIST_EXPORTER_PADS::WriteNetlist( const wxString& aOutFileName,
|
||||||
|
unsigned /* aNetlistOptions */,
|
||||||
|
REPORTER& aReporter )
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
FILE* f = nullptr;
|
||||||
|
|
||||||
|
if( ( f = wxFopen( aOutFileName, wxT( "wt" ) ) ) == nullptr )
|
||||||
|
{
|
||||||
|
wxString msg = wxString::Format( _( "Failed to create file '%s'." ), aOutFileName );
|
||||||
|
aReporter.Report( msg, RPT_SEVERITY_ERROR );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString msg;
|
||||||
|
wxString footprint;
|
||||||
|
SCH_SYMBOL* symbol;
|
||||||
|
|
||||||
|
ret |= fputs( "*PADS-PCB*\n", f );
|
||||||
|
ret |= fputs( "*PART*\n", f );
|
||||||
|
|
||||||
|
// Create netlist footprints section
|
||||||
|
m_referencesAlreadyFound.Clear();
|
||||||
|
|
||||||
|
SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
|
||||||
|
|
||||||
|
for( unsigned i = 0; i < sheetList.size(); i++ )
|
||||||
|
{
|
||||||
|
for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
|
||||||
|
{
|
||||||
|
symbol = findNextSymbol( item, &sheetList[ i ] );
|
||||||
|
|
||||||
|
if( !symbol )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( symbol->GetExcludedFromBoard() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
footprint = symbol->GetFootprintFieldText( true, &sheetList[ i ], false );
|
||||||
|
|
||||||
|
footprint = footprint.Trim( true );
|
||||||
|
footprint = footprint.Trim( false );
|
||||||
|
footprint.Replace( wxT( " " ), wxT( "_" ) );
|
||||||
|
|
||||||
|
if( footprint.IsEmpty() )
|
||||||
|
{
|
||||||
|
// fall back to value field
|
||||||
|
footprint = symbol->GetValueFieldText( true, &sheetList[i], false );
|
||||||
|
footprint.Replace( wxT( " " ), wxT( "_" ) );
|
||||||
|
footprint = footprint.Trim( true );
|
||||||
|
footprint = footprint.Trim( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = symbol->GetRef( &sheetList[i] );
|
||||||
|
ret |= fprintf( f, "%-16s %s\n", TO_UTF8( msg ), TO_UTF8( footprint ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret |= fputs( "\n", f );
|
||||||
|
|
||||||
|
if( !writeListOfNets( f ) )
|
||||||
|
ret = -1; // set error
|
||||||
|
|
||||||
|
fclose( f );
|
||||||
|
|
||||||
|
return ret >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool NETLIST_EXPORTER_PADS::writeListOfNets( FILE* f )
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int print_ter = 0;
|
||||||
|
|
||||||
|
wxString initialSignalLine;
|
||||||
|
wxString netName;
|
||||||
|
|
||||||
|
ret |= fputs( "*NET*\n", f );
|
||||||
|
|
||||||
|
for( const auto& [ key, subgraphs ] : m_schematic->ConnectionGraph()->GetNetMap() )
|
||||||
|
{
|
||||||
|
netName = key.Name;
|
||||||
|
|
||||||
|
std::vector<std::pair<SCH_PIN*, SCH_SHEET_PATH>> sorted_items;
|
||||||
|
|
||||||
|
for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
|
||||||
|
{
|
||||||
|
SCH_SHEET_PATH sheet = subgraph->GetSheet();
|
||||||
|
|
||||||
|
for( SCH_ITEM* item : subgraph->GetItems() )
|
||||||
|
{
|
||||||
|
if( item->Type() == SCH_PIN_T )
|
||||||
|
sorted_items.emplace_back( static_cast<SCH_PIN*>( item ), sheet );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Netlist ordering: Net name, then ref des, then pin name
|
||||||
|
std::sort( sorted_items.begin(), sorted_items.end(),
|
||||||
|
[]( std::pair<SCH_PIN*, SCH_SHEET_PATH> a, std::pair<SCH_PIN*, SCH_SHEET_PATH> b )
|
||||||
|
{
|
||||||
|
wxString ref_a = a.first->GetParentSymbol()->GetRef( &a.second );
|
||||||
|
wxString ref_b = b.first->GetParentSymbol()->GetRef( &b.second );
|
||||||
|
|
||||||
|
if( ref_a == ref_b )
|
||||||
|
return a.first->GetShownNumber() < b.first->GetShownNumber();
|
||||||
|
|
||||||
|
return ref_a < ref_b;
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Some duplicates can exist, for example on multi-unit parts with duplicated
|
||||||
|
// pins across units. If the user connects the pins on each unit, they will
|
||||||
|
// appear on separate subgraphs. Remove those here:
|
||||||
|
sorted_items.erase( std::unique( sorted_items.begin(), sorted_items.end(),
|
||||||
|
[]( std::pair<SCH_PIN*, SCH_SHEET_PATH> a, std::pair<SCH_PIN*, SCH_SHEET_PATH> b )
|
||||||
|
{
|
||||||
|
wxString ref_a = a.first->GetParentSymbol()->GetRef( &a.second );
|
||||||
|
wxString ref_b = b.first->GetParentSymbol()->GetRef( &b.second );
|
||||||
|
|
||||||
|
return ref_a == ref_b && a.first->GetShownNumber() == b.first->GetShownNumber();
|
||||||
|
} ),
|
||||||
|
sorted_items.end() );
|
||||||
|
|
||||||
|
print_ter = 0;
|
||||||
|
|
||||||
|
std::vector<wxString> netConns;
|
||||||
|
|
||||||
|
for( const std::pair<SCH_PIN*, SCH_SHEET_PATH>& pair : sorted_items )
|
||||||
|
{
|
||||||
|
SCH_PIN* pin = pair.first;
|
||||||
|
SCH_SHEET_PATH sheet = pair.second;
|
||||||
|
|
||||||
|
wxString refText = pin->GetParentSymbol()->GetRef( &sheet );
|
||||||
|
wxString pinText = pin->GetShownNumber();
|
||||||
|
|
||||||
|
// Skip power symbols and virtual symbols
|
||||||
|
if( refText[0] == wxChar( '#' ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
netConns.push_back(
|
||||||
|
wxString::Format( "%s.%.4s", refText, pinText ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// format it such that there are 6 net connections per line
|
||||||
|
// which seems to be the standard everyone follows
|
||||||
|
if( netConns .size() > 1 )
|
||||||
|
{
|
||||||
|
ret |= fprintf( f, "*SIGNAL* %s\n", TO_UTF8(netName) );
|
||||||
|
int cnt = 0;
|
||||||
|
for( wxString& netConn : netConns )
|
||||||
|
{
|
||||||
|
ret |= fputs( TO_UTF8( netConn ), f );
|
||||||
|
if( cnt != 0 && cnt % 6 == 0 )
|
||||||
|
{
|
||||||
|
ret |= fputc( '\n', f );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret |= fputc( ' ', f );
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret |= fputc( '\n', f );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret |= fprintf( f, "*END*\n" );
|
||||||
|
|
||||||
|
return ret >= 0;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 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-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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLIST_EXPORTER_PADS_H
|
||||||
|
#define NETLIST_EXPORTER_PADS_H
|
||||||
|
|
||||||
|
#include "netlist_exporter_base.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a netlist compatible with PADS.
|
||||||
|
*/
|
||||||
|
class NETLIST_EXPORTER_PADS : public NETLIST_EXPORTER_BASE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NETLIST_EXPORTER_PADS( SCHEMATIC* aSchematic ) :
|
||||||
|
NETLIST_EXPORTER_BASE( aSchematic )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to specified output file.
|
||||||
|
*/
|
||||||
|
bool WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
|
||||||
|
REPORTER& aReporter ) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Write a net list (ranked by Netcode), and pins connected to it.
|
||||||
|
*
|
||||||
|
* Format:
|
||||||
|
* - ADD_TER RR2 6 \"$42\"
|
||||||
|
* - B U1 100
|
||||||
|
* - 6 CA
|
||||||
|
*/
|
||||||
|
bool writeListOfNets( FILE* f );
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -42,6 +42,7 @@
|
||||||
#include <netlist_exporter_spice_model.h>
|
#include <netlist_exporter_spice_model.h>
|
||||||
#include <netlist_exporter_kicad.h>
|
#include <netlist_exporter_kicad.h>
|
||||||
#include <netlist_exporter_allegro.h>
|
#include <netlist_exporter_allegro.h>
|
||||||
|
#include <netlist_exporter_pads.h>
|
||||||
#include <netlist_exporter_xml.h>
|
#include <netlist_exporter_xml.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,7 +92,11 @@ bool SCH_EDIT_FRAME::WriteNetListFile( int aFormat, const wxString& aFullFileNam
|
||||||
|
|
||||||
case NET_TYPE_ALLEGRO:
|
case NET_TYPE_ALLEGRO:
|
||||||
helper = new NETLIST_EXPORTER_ALLEGRO( sch );
|
helper = new NETLIST_EXPORTER_ALLEGRO( sch );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NET_TYPE_PADS:
|
||||||
|
helper = new NETLIST_EXPORTER_PADS( sch );
|
||||||
|
break;
|
||||||
|
|
||||||
case NET_TYPE_BOM:
|
case NET_TYPE_BOM:
|
||||||
// When generating the BOM, we have a bare filename so don't strip
|
// When generating the BOM, we have a bare filename so don't strip
|
||||||
|
|
|
@ -135,6 +135,7 @@ public:
|
||||||
static const std::string OrCadPcb2NetlistFileExtension;
|
static const std::string OrCadPcb2NetlistFileExtension;
|
||||||
static const std::string NetlistFileExtension;
|
static const std::string NetlistFileExtension;
|
||||||
static const std::string AllegroNetlistFileExtension;
|
static const std::string AllegroNetlistFileExtension;
|
||||||
|
static const std::string PADSNetlistFileExtension;
|
||||||
static const std::string GerberFileExtension;
|
static const std::string GerberFileExtension;
|
||||||
static const std::string GerberJobFileExtension;
|
static const std::string GerberJobFileExtension;
|
||||||
static const std::string HtmlFileExtension;
|
static const std::string HtmlFileExtension;
|
||||||
|
@ -219,6 +220,7 @@ public:
|
||||||
static wxString OrCadPcb2NetlistFileWildcard();
|
static wxString OrCadPcb2NetlistFileWildcard();
|
||||||
static wxString NetlistFileWildcard();
|
static wxString NetlistFileWildcard();
|
||||||
static wxString AllegroNetlistFileWildcard();
|
static wxString AllegroNetlistFileWildcard();
|
||||||
|
static wxString PADSNetlistFileWildcard();
|
||||||
static wxString HtmlFileWildcard();
|
static wxString HtmlFileWildcard();
|
||||||
static wxString CsvFileWildcard();
|
static wxString CsvFileWildcard();
|
||||||
static wxString PcbFileWildcard();
|
static wxString PcbFileWildcard();
|
||||||
|
|
Loading…
Reference in New Issue