/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014-2017 Cirilo Bernardo * Copyright (C) 2021 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 #include #include #include #include #include #include #include #include #include "idf_parser.h" #include "idf_helpers.h" #include "streamwrapper.h" using namespace std; using namespace IDF3; static bool MatchCompOutline( IDF3_COMP_OUTLINE* aOutlineA, IDF3_COMP_OUTLINE* aOutlineB ) { if( aOutlineA->GetComponentClass() != aOutlineB->GetComponentClass() ) return false; if( aOutlineA->OutlinesSize() != aOutlineB->OutlinesSize() ) return false; // are both outlines empty? if( aOutlineA->OutlinesSize() == 0 ) return true; IDF_OUTLINE* opA = aOutlineA->GetOutline( 0 ); IDF_OUTLINE* opB = aOutlineB->GetOutline( 0 ); if( opA->size() != opB->size() ) return false; if( opA->size() == 0 ) return true; std::list::iterator olAs = opA->begin(); std::list::iterator olAe = opA->end(); std::list::iterator olBs = opB->begin(); while( olAs != olAe ) { if( !(*olAs)->MatchesStart( (*olBs)->startPoint ) ) return false; if( !(*olAs)->MatchesEnd( (*olBs)->endPoint ) ) return false; ++olAs; ++olBs; } return true; } IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA() { parent = nullptr; outline = nullptr; xoff = 0.0; yoff = 0.0; zoff = 0.0; aoff = 0.0; } IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline ) { parent = aParent; outline = aOutline; xoff = 0.0; yoff = 0.0; zoff = 0.0; aoff = 0.0; if( aOutline ) aOutline->incrementRef(); } IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline, double aXoff, double aYoff, double aZoff, double aAngleOff ) { parent = aParent; outline = aOutline; xoff = aXoff; yoff = aYoff; zoff = aZoff; aoff = aAngleOff; } IDF3_COMP_OUTLINE_DATA::~IDF3_COMP_OUTLINE_DATA() { if( outline ) outline->decrementRef(); } #ifndef DISABLE_IDF_OWNERSHIP bool IDF3_COMP_OUTLINE_DATA::checkOwnership( int aSourceLine, const char* aSourceFunc ) { if( !parent ) { ostringstream ostr; ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n"; ostr << "* BUG: IDF3_COMP_OUTLINE_DATA::parent not set; cannot enforce ownership rules\n"; errormsg = ostr.str(); return false; } IDF3::IDF_PLACEMENT placement = parent->GetPlacement(); IDF3::CAD_TYPE parentCAD = parent->GetCadType(); if( placement == PS_PLACED || placement == PS_UNPLACED ) return true; if( placement == PS_MCAD && parentCAD == CAD_MECH ) return true; if( placement == PS_ECAD && parentCAD == CAD_ELEC ) return true; do { ostringstream ostr; ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n"; ostr << "* ownership violation; CAD type is "; if( parentCAD == CAD_MECH ) ostr << "MCAD "; else ostr << "ECAD "; ostr << "while outline owner is " << GetPlacementString( placement ) << "\n"; errormsg = ostr.str(); } while( 0 ); return false; } #endif bool IDF3_COMP_OUTLINE_DATA::SetOffsets( double aXoff, double aYoff, double aZoff, double aAngleOff ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif xoff = aXoff; yoff = aYoff; zoff = aZoff; aoff = aAngleOff; return true; } void IDF3_COMP_OUTLINE_DATA::GetOffsets( double& aXoff, double& aYoff, double& aZoff, double& aAngleOff ) { aXoff = xoff; aYoff = yoff; aZoff = zoff; aAngleOff = aoff; } void IDF3_COMP_OUTLINE_DATA::SetParent( IDF3_COMPONENT* aParent ) { parent = aParent; } bool IDF3_COMP_OUTLINE_DATA::SetOutline( IDF3_COMP_OUTLINE* aOutline ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif if( outline ) outline->decrementRef(); outline = aOutline; if( outline ) outline->incrementRef(); return true; } bool IDF3_COMP_OUTLINE_DATA::readPlaceData( std::istream &aBoardFile, IDF3::FILE_STATE& aBoardState, IDF3_BOARD *aBoard, IDF3::IDF_VERSION aIdfVersion, bool aNoSubstituteOutlines ) { if( !aBoard ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* BUG: invoked with no reference to the parent IDF_BOARD" ) ); // clear out data possibly left over from previous use of the object outline = nullptr; parent = nullptr; std::string iline; // the input line bool isComment; // true if a line just read in is a comment line std::streampos pos; int idx = 0; bool quoted = false; std::string token; std::string uid; std::string refdes; IDF3::IDF_PLACEMENT placement = IDF3::PS_UNPLACED; IDF3::IDF_LAYER side = IDF3::LYR_TOP; // RECORD 2: 'package name', 'part number', 'Refdes' (any, NOREFDES, BOARD) while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() ); if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: could not read PLACEMENT section\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( isComment ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: comment within PLACEMENT section\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } idx = 0; GetIDFString( iline, token, quoted, idx ); if( !quoted && CompareToken( ".END_PLACEMENT", token ) ) { aBoardState = IDF3::FILE_PLACEMENT; return false; } std::string ngeom = token; if( !GetIDFString( iline, token, quoted, idx ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: no PART NAME in PLACEMENT RECORD2\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } std::string npart = token; uid = ngeom + "_" + npart; if( !GetIDFString( iline, token, quoted, idx ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: no REFDES in PLACEMENT RECORD2\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( CompareToken( "NOREFDES", token ) ) { // according to the IDF3.0 specification, this is a // mechanical component. The specification is defective // since it is impossible to associate mechanical // components with their holes unless the mechanical // component is given a unique RefDes. This class of defect // is one reason IDF does not work well in faithfully // conveying information between ECAD and MCAD. refdes = aBoard->GetNewRefDes(); } else if( CompareToken( "BOARD", token ) ) { ostringstream ostr; ostr << "UNSUPPORTED FEATURE\n"; ostr << "* RefDes is 'BOARD', indicating this is a PANEL FILE (not supported)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } else if( CompareToken( "PANEL", token ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: RefDes in PLACEMENT RECORD2 is 'PANEL'\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } else if( token.empty() ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: empty RefDes string in PLACEMENT RECORD2\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } else { // note: perversely, spaces can be a valid RefDes refdes = token; } // V2: RECORD 3: X, Y, ROT, SIDE (top/bot), PLACEMENT (fixed, placed, unplaced) // V3: RECORD 3: X, Y, Z, ROT, SIDE (top/bot), PLACEMENT (placed, unplaced, mcad, ecad) while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() ); if( !aBoardFile.good() ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* problems reading PLACEMENT SECTION, RECORD 3\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( isComment ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: comment within PLACEMENT section\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } idx = 0; GetIDFString( iline, token, quoted, idx ); if( quoted ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: X value must not be in quotes (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } istringstream istr; istr.str( token ); istr >> xoff; if( istr.fail() ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: X value is not numeric (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !GetIDFString( iline, token, quoted, idx ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: no Y value (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } istr.clear(); istr.str( token ); istr >> yoff; if( istr.fail() ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: Y value is not numeric (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( aIdfVersion > IDF_V2 ) { if( !GetIDFString( iline, token, quoted, idx ) ) { ostringstream ostr; ostr << "invalid IDFv3 file\n"; ostr << "* violation: no Z value (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } istr.clear(); istr.str( token ); istr >> zoff; if( istr.fail() ) { ostringstream ostr; ostr << "invalid IDFv3 file\n"; ostr << "* violation: Z value is not numeric (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } } if( !GetIDFString( iline, token, quoted, idx ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: no rotation value (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } istr.clear(); istr.str( token ); istr >> aoff; if( istr.fail() ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: rotation value is not numeric (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !GetIDFString( iline, token, quoted, idx ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: no SIDE value (PLACEMENT RECORD 3)\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( CompareToken( "TOP", token ) ) { side = IDF3::LYR_TOP; } else if( CompareToken( "BOTTOM", token ) ) { side = IDF3::LYR_BOTTOM; } else { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: invalid SIDE value in PLACEMENT RECORD 3 ('"; ostr << token << "'); must be one of TOP/BOTTOM\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !GetIDFString( iline, token, quoted, idx ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: no PLACEMENT value in PLACEMENT RECORD 3\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( CompareToken( "PLACED", token ) ) { placement = IDF3::PS_PLACED; } else if( CompareToken( "UNPLACED", token ) ) { placement = IDF3::PS_UNPLACED; } else if( aIdfVersion > IDF_V2 && CompareToken( "MCAD", token ) ) { placement = IDF3::PS_MCAD; } else if( aIdfVersion > IDF_V2 && CompareToken( "ECAD", token ) ) { placement = IDF3::PS_ECAD; } else if( aIdfVersion < IDF_V3 && CompareToken( "FIXED", token ) ) { if( aBoard->GetCadType() == CAD_ELEC ) placement = IDF3::PS_MCAD; else placement = IDF3::PS_ECAD; } else { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: invalid PLACEMENT value ('"; ostr << token << "') in PLACEMENT RECORD 3\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } outline = aBoard->GetComponentOutline( uid ); if( outline == nullptr && !aNoSubstituteOutlines ) { ERROR_IDF << "MISSING OUTLINE\n"; cerr << "* GeomName( " << ngeom << " ), PartName( " << npart << " )\n"; cerr << "* Substituting default outline.\n"; outline = aBoard->GetInvalidOutline( ngeom, npart ); if( outline == nullptr ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* missing outline: cannot create default" ) ); } if( aBoard->GetUnit() == IDF3::UNIT_THOU ) { xoff *= IDF_THOU_TO_MM; yoff *= IDF_THOU_TO_MM; zoff *= IDF_THOU_TO_MM; } parent = aBoard->FindComponent( refdes ); if( parent == nullptr ) { IDF3_COMPONENT* cp; try { cp = new IDF3_COMPONENT( aBoard ); } catch( std::bad_alloc& ) { outline = nullptr; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "cannot create component object" ) ); } cp->SetRefDes( refdes ); cp->SetPosition( xoff, yoff, aoff, side ); cp->SetPlacement( placement ); xoff = 0; yoff = 0; aoff = 0; aBoard->AddComponent( cp ); parent = cp; } else { double tX, tY, tA; IDF3::IDF_LAYER tL; if( parent->GetPosition( tX, tY, tA, tL ) ) { if( side != tL ) { outline = nullptr; ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: inconsistent PLACEMENT data; "; ostr << "* SIDE value has changed from " << GetLayerString( tL ); ostr << " to " << GetLayerString( side ) << "\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } xoff -= tX; yoff -= tY; aoff -= tA; } else { parent->SetPosition( xoff, yoff, aoff, side ); parent->SetPlacement( placement ); xoff = 0; yoff = 0; aoff = 0; } if( placement != parent->GetPlacement() ) { outline = nullptr; ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* violation: inconsistent PLACEMENT data; "; ostr << "* PLACEMENT value has changed from "; ostr << GetPlacementString( parent->GetPlacement() ); ostr << " to " << GetPlacementString( placement ) << "\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* file position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } } // copy internal data to a new object and push it into the component's outline list IDF3_COMP_OUTLINE_DATA* cdp = new IDF3_COMP_OUTLINE_DATA; *cdp = *this; if( outline ) outline->incrementRef(); outline = nullptr; if( !parent->AddOutlineData( cdp ) ) { delete cdp; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not add outline data object" ) ); } return true; } void IDF3_COMP_OUTLINE_DATA::writePlaceData( std::ostream& aBoardFile, double aXpos, double aYpos, double aAngle, const std::string& aRefDes, IDF3::IDF_PLACEMENT aPlacement, IDF3::IDF_LAYER aSide ) { if( outline == nullptr ) return; if( outline->GetUID().empty() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "empty GEOM and PART names" ) ); if( aPlacement == PS_INVALID ) { ERROR_IDF << "placement invalid (" << aRefDes << ":"; aPlacement = PS_PLACED; } if( aSide != LYR_TOP && aSide != LYR_BOTTOM ) { ostringstream ostr; ostr << "\n* invalid side (" << GetLayerString( aSide ) << "); "; ostr << "must be TOP or BOTTOM\n"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } // calculate the final position based on layer double xpos, ypos, ang; switch( aSide ) { case LYR_TOP: xpos = aXpos + xoff; ypos = aYpos + yoff; ang = aAngle + aoff; break; default: xpos = aXpos - xoff; ypos = aYpos + yoff; ang = aAngle - aoff; break; } std::string arefdes = aRefDes; if( arefdes.empty() || !arefdes.compare( "~" ) || ( arefdes.size() >= 8 && CompareToken( "NOREFDES", arefdes.substr(0, 8) ) ) ) arefdes = "NOREFDES"; aBoardFile << "\"" << outline->GetGeomName() << "\" \"" << outline->GetPartName() << "\" " << arefdes << "\n"; IDF3::IDF_UNIT unit = UNIT_MM; if( parent ) unit = parent->GetUnit(); if( unit == UNIT_MM ) { aBoardFile << setiosflags(ios::fixed) << setprecision(5) << xpos << " " << ypos << " " << setprecision(3) << zoff << " " << ang << " "; } else { aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (xpos / IDF_THOU_TO_MM) << " " << (ypos / IDF_THOU_TO_MM) << " " << (zoff / IDF_THOU_TO_MM) << " " << setprecision(3) << ang << " "; } WriteLayersText( aBoardFile, aSide ); switch( aPlacement ) { case PS_PLACED: aBoardFile << " PLACED\n"; break; case PS_UNPLACED: aBoardFile << " UNPLACED\n"; break; case PS_MCAD: aBoardFile << " MCAD\n"; break; default: aBoardFile << " ECAD\n"; break; } } IDF3_COMPONENT::IDF3_COMPONENT( IDF3_BOARD* aParent ) { xpos = 0.0; ypos = 0.0; angle = 0.0; hasPosition = false; placement = PS_INVALID; layer = LYR_INVALID; parent = aParent; } IDF3_COMPONENT::~IDF3_COMPONENT() { std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcS = components.begin(); std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcE = components.end(); while( itcS != itcE ) { delete *itcS; ++itcS; } components.clear(); std::list< IDF_DRILL_DATA* >::iterator itdS = drills.begin(); std::list< IDF_DRILL_DATA* >::iterator itdE = drills.end(); while( itdS != itdE ) { delete *itdS; ++itdS; } drills.clear(); } #ifndef DISABLE_IDF_OWNERSHIP bool IDF3_COMPONENT::checkOwnership( int aSourceLine, const char* aSourceFunc ) { if( !parent ) { ostringstream ostr; ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n"; ostr << "\n* BUG: parent not set"; errormsg = ostr.str(); return false; } IDF3::CAD_TYPE pcad = parent->GetCadType(); switch( placement ) { case PS_UNPLACED: case PS_PLACED: case PS_INVALID: break; case PS_MCAD: if( pcad != CAD_MECH ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with " "PLACEMENT ("; ostr << GetPlacementString( placement ) << ")"; errormsg = ostr.str(); return false; } break; case PS_ECAD: if( pcad != CAD_ELEC ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with " "PLACEMENT ("; ostr << GetPlacementString( placement ) << ")"; errormsg = ostr.str(); return false; } break; default: do { ostringstream ostr; ostr << "\n* BUG: unhandled internal placement value (" << placement << ")"; errormsg = ostr.str(); return false; } while( 0 ); break; } return true; } #endif void IDF3_COMPONENT::SetParent( IDF3_BOARD* aParent ) { parent = aParent; return; } IDF3::CAD_TYPE IDF3_COMPONENT::GetCadType( void ) { if( parent ) return parent->GetCadType(); return CAD_INVALID; } IDF3::IDF_UNIT IDF3_COMPONENT::GetUnit( void ) { if( parent ) return parent->GetUnit(); return UNIT_INVALID; } bool IDF3_COMPONENT::SetRefDes( const std::string& aRefDes ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif if( aRefDes.empty() ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid RefDes (empty)"; errormsg = ostr.str(); return false; } if( CompareToken( "PANEL", aRefDes ) ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* BUG: PANEL is a reserved designator and may not be used by components"; errormsg = ostr.str(); return false; } refdes = aRefDes; return true; } const std::string& IDF3_COMPONENT::GetRefDes( void ) { return refdes; } IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( double aDia, double aXpos, double aYpos, IDF3::KEY_PLATING aPlating, const std::string& aHoleType, IDF3::KEY_OWNER aOwner ) { IDF_DRILL_DATA* dp = nullptr; try { dp = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating, refdes, aHoleType, aOwner ); } catch( std::bad_alloc& ) { return nullptr; } drills.push_back( dp ); return dp; } IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( IDF_DRILL_DATA* aDrilledHole ) { if( !aDrilledHole ) return nullptr; if( CompareToken( "PANEL", refdes ) ) { ERROR_IDF; cerr << "\n* BUG: PANEL drills not supported at component level\n"; return nullptr; } if( refdes.compare( aDrilledHole->GetDrillRefDes() ) ) { ERROR_IDF; cerr << "\n* BUG: pushing an incorrect REFDES ('" << aDrilledHole->GetDrillRefDes(); cerr << "') to component ('" << refdes << "')\n"; return nullptr; } drills.push_back( aDrilledHole ); return aDrilledHole; } bool IDF3_COMPONENT::DelDrill( double aDia, double aXpos, double aYpos ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif errormsg.clear(); if( drills.empty() ) return false; bool val = false; list< IDF_DRILL_DATA* >::iterator itS = drills.begin(); list< IDF_DRILL_DATA* >::iterator itE = drills.end(); while( !drills.empty() && itS != itE ) { if( (*itS)->Matches( aDia, aXpos, aYpos ) ) { val = true; delete *itS; itS = drills.erase( itS ); continue; } ++itS; } return val; } bool IDF3_COMPONENT::DelDrill( IDF_DRILL_DATA* aDrill ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif errormsg.clear(); if( drills.empty() ) return false; list< IDF_DRILL_DATA* >::iterator itS = drills.begin(); list< IDF_DRILL_DATA* >::iterator itE = drills.end(); while( !drills.empty() && itS != itE ) { if( *itS == aDrill ) { delete *itS; drills.erase( itS ); return true; } ++itS; } return false; } const std::list< IDF_DRILL_DATA* >*const IDF3_COMPONENT::GetDrills( void ) { return &drills; } bool IDF3_COMPONENT::AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline ) { if( aComponentOutline == nullptr ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (nullptr)"; errormsg = ostr.str(); return false; } components.push_back( aComponentOutline ); return true; } bool IDF3_COMPONENT::DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif if( components.empty() ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): component list is empty"; errormsg = ostr.str(); return false; } if( aComponentOutline == nullptr ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (nullptr)"; errormsg = ostr.str(); return false; } errormsg.clear(); std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin(); std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end(); while( itS != itE ) { if( *itS == aComponentOutline ) { delete *itS; components.erase( itS ); return true; } ++itS; } return false; } bool IDF3_COMPONENT::DeleteOutlineData( size_t aIndex ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif if( aIndex >= components.size() ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* aIndex (" << aIndex << ") out of range; list size is " << components.size(); errormsg = ostr.str(); return false; } std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin(); std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end(); size_t idx = 0; while( itS != itE ) { if( idx == aIndex ) { delete *itS; components.erase( itS ); return true; } ++idx; ++itS; } return false; } size_t IDF3_COMPONENT::GetOutlinesSize( void ) { return components.size(); } const std::list< IDF3_COMP_OUTLINE_DATA* >*const IDF3_COMPONENT::GetOutlinesData( void ) { return &components; } bool IDF3_COMPONENT::GetPosition( double& aXpos, double& aYpos, double& aAngle, IDF3::IDF_LAYER& aLayer ) { errormsg.clear(); if( !hasPosition ) { aXpos = 0.0; aYpos = 0.0; aAngle = 0.0; aLayer = IDF3::LYR_INVALID; return false; } aXpos = xpos; aYpos = ypos; aAngle = angle; aLayer = layer; return true; } bool IDF3_COMPONENT::SetPosition( double aXpos, double aYpos, double aAngle, IDF3::IDF_LAYER aLayer ) { #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif errormsg.clear(); switch( aLayer ) { case LYR_TOP: case LYR_BOTTOM: break; default: do { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "\n* invalid side (must be TOP or BOTTOM only): " << GetLayerString( aLayer ); errormsg = ostr.str(); return false; } while( 0 ); break; } if( hasPosition ) return false; hasPosition = true; xpos = aXpos; ypos = aYpos; angle = aAngle; layer = aLayer; return true; } IDF3::IDF_PLACEMENT IDF3_COMPONENT::GetPlacement( void ) { return placement; } bool IDF3_COMPONENT::SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue ) { if( aPlacementValue < PS_UNPLACED || aPlacementValue >= PS_INVALID ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "\n* invalid PLACEMENT value (" << aPlacementValue << ")"; errormsg = ostr.str(); return false; } #ifndef DISABLE_IDF_OWNERSHIP if( !checkOwnership( __LINE__, __FUNCTION__ ) ) return false; #endif placement = aPlacementValue; return true; } bool IDF3_COMPONENT::writeDrillData( std::ostream& aBoardFile ) { if( drills.empty() ) return true; std::list< IDF_DRILL_DATA* >::iterator itS = drills.begin(); std::list< IDF_DRILL_DATA* >::iterator itE = drills.end(); while( itS != itE ) { (*itS)->write( aBoardFile, GetUnit() ); ++itS; } return true; } bool IDF3_COMPONENT::writePlaceData( std::ostream& aBoardFile ) { if( components.empty() ) return true; std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin(); std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end(); while( itS != itE ) { (*itS)->writePlaceData( aBoardFile, xpos, ypos, angle, refdes, placement, layer ); ++itS; } return true; } IDF3_BOARD::IDF3_BOARD( IDF3::CAD_TYPE aCadType ) { idfVer = IDF_V3; cadType = aCadType; userPrec = 5; userScale = 1.0; userXoff = 0.0; userYoff = 0.0; brdFileVersion = 0; libFileVersion = 0; iRefDes = 0; unit = UNIT_MM; // unlike other outlines which are created as necessary, // the board outline always exists and its parent must // be set here olnBoard.setParent( this ); olnBoard.setThickness( 1.6 ); return; } IDF3_BOARD::~IDF3_BOARD() { Clear(); return; } const std::string& IDF3_BOARD::GetNewRefDes( void ) { ostringstream ostr; ostr << "NOREFDESn" << iRefDes++; sRefDes = ostr.str(); return sRefDes; } #ifndef DISABLE_IDF_OWNERSHIP bool IDF3_BOARD::checkComponentOwnership( int aSourceLine, const char* aSourceFunc, IDF3_COMPONENT* aComponent ) { if( !aComponent ) { ostringstream ostr; ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc; ostr << "(): Invalid component pointer (nullptr)"; errormsg = ostr.str(); return false; } IDF3::IDF_PLACEMENT place = aComponent->GetPlacement(); if( place == PS_PLACED || place == PS_UNPLACED ) return true; if( place == PS_MCAD && cadType == CAD_MECH ) return true; if( place == PS_ECAD && cadType == CAD_ELEC ) return true; do { ostringstream ostr; ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n"; ostr << "* ownership violation; CAD type is "; if( cadType == CAD_MECH ) ostr << "MCAD "; else ostr << "ECAD "; ostr << "while outline owner is " << GetPlacementString( place ) << "\n"; errormsg = ostr.str(); } while( 0 ); return false; } #endif IDF3::CAD_TYPE IDF3_BOARD::GetCadType( void ) { return cadType; } void IDF3_BOARD::SetBoardName( const std::string& aBoardName ) { boardName = std::move( aBoardName ); } const std::string& IDF3_BOARD::GetBoardName( void ) { return boardName; } bool IDF3_BOARD::setUnit( IDF3::IDF_UNIT aUnit, bool convert ) { switch( aUnit ) { case UNIT_MM: case UNIT_THOU: unit = aUnit; break; case UNIT_TNM: ERROR_IDF << "\n* TNM unit is not supported; defaulting to mm\n"; unit = UNIT_MM; break; default: do { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* invalid board unit (" << aUnit << ")"; errormsg = ostr.str(); return false; } while( 0 ); break; } // iterate through all owned OUTLINE objects (except IDF3_COMP_OUTLINE) // and set to the same unit olnBoard.SetUnit( aUnit ); do { std::map< std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin(); std::map< std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end(); while( its != ite ) { its->second->SetUnit( aUnit ); ++its; } } while( 0 ); do { std::list::iterator its = olnRoute.begin(); std::list::iterator ite = olnRoute.end(); while( its != ite ) { (*its)->SetUnit( aUnit ); ++its; } } while( 0 ); do { std::list::iterator its = olnPlace.begin(); std::list::iterator ite = olnPlace.end(); while( its != ite ) { (*its)->SetUnit( aUnit ); ++its; } } while( 0 ); do { std::list::iterator its = olnRouteKeepout.begin(); std::list::iterator ite = olnRouteKeepout.end(); while( its != ite ) { (*its)->SetUnit( aUnit ); ++its; } } while( 0 ); do { std::list::iterator its = olnViaKeepout.begin(); std::list::iterator ite = olnViaKeepout.end(); while( its != ite ) { (*its)->SetUnit( aUnit ); ++its; } } while( 0 ); do { std::list::iterator its = olnPlaceKeepout.begin(); std::list::iterator ite = olnPlaceKeepout.end(); while( its != ite ) { (*its)->SetUnit( aUnit ); ++its; } } while( 0 ); do { std::multimap::iterator its = olnGroup.begin(); std::multimap::iterator ite = olnGroup.end(); while( its != ite ) { its->second->SetUnit( aUnit ); ++its; } } while( 0 ); //iterate through all owned IDF3_COMP_OUTLINE objects and // set to the same unit IF convert = true if( convert ) { std::map::iterator its = compOutlines.begin(); std::map::iterator ite = compOutlines.end(); while( its != ite ) { its->second->SetUnit( aUnit ); ++its; } } return true; } IDF3::IDF_UNIT IDF3_BOARD::GetUnit( void ) { return unit; } bool IDF3_BOARD::SetBoardThickness( double aBoardThickness ) { if( aBoardThickness <= 0.0 ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "; ostr << "board thickness (" << aBoardThickness << ") must be > 0"; errormsg = ostr.str(); return false; } if( !olnBoard.SetThickness( aBoardThickness ) ) { errormsg = olnBoard.GetError(); return false; } return true; } double IDF3_BOARD::GetBoardThickness( void ) { return olnBoard.GetThickness(); } void IDF3_BOARD::readBrdDrills( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState ) { IDF_DRILL_DATA drill; while( drill.read( aBoardFile, unit, aBoardState, idfVer ) ) { IDF_DRILL_DATA *dp = new IDF_DRILL_DATA; *dp = drill; if( AddDrill( dp ) == nullptr ) { delete dp; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* BUG: could not add drill data; cannot continue reading the " "file" ) ); } } } void IDF3_BOARD::readBrdNotes( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState ) { IDF_NOTE note; while( note.readNote( aBoardFile, aBoardState, unit ) ) { IDF_NOTE *np = new IDF_NOTE; *np = note; notes.push_back( np ); } } void IDF3_BOARD::readBrdPlacement( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState, bool aNoSubstituteOutlines ) { IDF3_COMP_OUTLINE_DATA oldata; while( oldata.readPlaceData( aBoardFile, aBoardState, this, idfVer, aNoSubstituteOutlines ) ); } void IDF3_BOARD::readBrdHeader( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState ) { std::string iline; // the input line bool isComment; // true if a line just read in is a comment line std::streampos pos; int idx = 0; bool quoted = false; std::string token; // RECORD 1: ".HEADER" must be the very first line while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() ); if( !aBoardFile.good() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading board header" ) ); if( isComment ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: first line must be .HEADER\n" ) ); if( !CompareToken( ".HEADER", iline ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* first line must be .HEADER and have no quotes or trailing text" ) ); // RECORD 2: // File Type [str]: BOARD_FILE (PANEL_FILE not supported) // IDF Version Number [float]: must be 3.0 // Source System [str]: ignored // Date [str]: ignored // Board File Version [int]: ignored while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() ); if( !aBoardFile.good() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading board header, RECORD 2" ) ); if( isComment ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: comment within .HEADER section" ) ); idx = 0; GetIDFString( iline, token, quoted, idx ); if( quoted ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* File Type in HEADER section must not be in quotes" ) ); if( !CompareToken( "BOARD_FILE", token ) ) { ERROR_IDF; if( CompareToken( "PANEL_FILE", token ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "not a board file\n" "* PANEL_FILE is not supported (expecting BOARD_FILE)" ) ); else throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Expecting string: BOARD_FILE" ) ); } if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) ); if( quoted ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: IDF Version must not be in quotes" ) ); if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) ) { idfVer = IDF_V3; } else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) ) { idfVer = IDF_V2; } else { ostringstream ostr; ostr << "unsupported IDF version\n"; ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) ); brdSource = token; if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 4: no Date string" ) ); brdDate = token; if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) ); std::istringstream istr; istr.str( token ); istr >> brdFileVersion; if( istr.fail() ) { ERROR_IDF << "invalid Board File Version in header\n"; cerr << "* Setting default version of 1\n"; brdFileVersion = 1; } if( quoted ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 5: Board File Version must not be " "in quotes" ) ); // RECORD 3: // Board Name [str]: stored // Units [str]: MM or THOU while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() ); if( !aBoardFile.good() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* problems reading board header, RECORD 2" ) ); if( isComment ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: comment within .HEADER section" ) ); idx = 0; GetIDFString( iline, token, quoted, idx ); boardName = token; if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* HEADER section, RECORD 3, FIELD 1: no Board Name" ) ); if( quoted ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification:\n" "* HEADER section, RECORD 3, FIELD 2: UNIT may not be in quotes" ) ); if( CompareToken( "MM", token ) ) { unit = IDF3::UNIT_MM; } else if( CompareToken( "THOU", token ) ) { unit = IDF3::UNIT_THOU; } else if( ( idfVer == IDF_V2 ) && CompareToken( "TNM", token ) ) { unit = IDF3::UNIT_TNM; } else { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* HEADER section, RECORD 3, FIELD 2: expecting MM or THOU (got '" << token << "')\n"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } olnBoard.SetUnit( unit ); // RECORD 4: // .END_HEADER while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() ); if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading board header, RECORD 4" ) ); if( isComment ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: comment within .HEADER section\n" ) ); if( !CompareToken( ".END_HEADER", iline ) ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* Violation of specification: expected .END_HEADER\n"; ostr << "* line: '" << iline << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } aBoardState = IDF3::FILE_HEADER; } void IDF3_BOARD::readBrdSection( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState, bool aNoSubstituteOutlines ) { std::list< std::string > comments; // comments associated with a section // Reads in .SECTION_ID or #COMMENTS // Expected SECTION IDs: // .BOARD_OUTLINE // .PANEL_OUTLINE (NOT SUPPORTED) // .OTHER_OUTLINE // .ROUTE_OUTLINE // .PLACE_OUTLINE // .ROUTE_KEEPOUT // .VIA_KEEPOUT // .PLACE_KEEPOUT // .PLACE_REGION // .DRILLED_HOLES // .NOTES // .PLACEMENT std::string iline; // the input line bool isComment; // true if a line just read in is a comment line std::streampos pos; int idx = 0; bool quoted = false; std::string token; while( aBoardFile.good() ) { while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() ); if( !aBoardFile.good() ) { if( aBoardFile.eof() && aBoardState >= IDF3::FILE_HEADER && aBoardState < IDF3::FILE_INVALID ) { if( !comments.empty() ) ERROR_IDF << "[warning]: trailing comments in IDF file (comments will be " "lost)\n"; return; } throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading board section" ) ); } if( isComment ) { comments.push_back( iline ); continue; } // This must be a header GetIDFString( iline, token, quoted, idx ); if( quoted ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* Violation of specification: quoted string where SECTION HEADER expected\n"; ostr << "* line: '" << iline << "'"; ostr << "* position: " << pos; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( CompareToken( ".BOARD_OUTLINE", token ) ) { if( aBoardState != IDF3::FILE_HEADER ) { aBoardState = IDF3::FILE_INVALID; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: no HEADER section" ) ); } olnBoard.readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { olnBoard.AddComment( *its ); ++its; } } aBoardState = IDF3::FILE_OUTLINE; return; } if( CompareToken( ".PANEL_OUTLINE", token ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "PANEL_OUTLINE not supported" ) ); if( CompareToken( ".OTHER_OUTLINE", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".OTHER_OUTLINE" ) ); OTHER_OUTLINE* op = nullptr; try { op = new OTHER_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not create OTHER_OUTLINE object" ) ); } if( op == nullptr ) op->SetUnit( unit ); op->readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { op->AddComment( *its ); ++its; } } if( olnOther.insert( pair(op->GetOutlineIdentifier(), op ) ).second == false ) { ostringstream ostr; ostr << "invalid IDF file\n"; ostr << "* Violation of specification. Non-unique ID in OTHER_OUTLINE '"; ostr << op->GetOutlineIdentifier() << "'\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* pos: " << pos; delete op; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } return; } if( CompareToken( ".ROUTE_OUTLINE", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".ROUTE_OUTLINE" ) ); ROUTE_OUTLINE* op = nullptr; try { op = new ROUTE_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not create ROUTE_OUTLINE object" ) ); } op->SetUnit( unit ); op->readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { op->AddComment( *its ); ++its; } } olnRoute.push_back( op ); return; } if( CompareToken( ".PLACE_OUTLINE", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".PLACE_OUTLINE" ) ); PLACE_OUTLINE* op = nullptr; try { op = new PLACE_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not create PLACE_OUTLINE object" ) ); } op->SetUnit( unit ); op->readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { op->AddComment( *its ); ++its; } } olnPlace.push_back( op ); return; } if( CompareToken( ".ROUTE_KEEPOUT", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".ROUTE_KEEPOUT" ) ); ROUTE_KO_OUTLINE* op = nullptr; try { op = new ROUTE_KO_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not create ROUTE_KEEPOUT object" ) ); } op->SetUnit( unit ); op->readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { op->AddComment( *its ); ++its; } } olnRouteKeepout.push_back( op ); return; } if( CompareToken( ".VIA_KEEPOUT", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".VIA_KEEPOUT" ) ); VIA_KO_OUTLINE* op = nullptr; try { op = new VIA_KO_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not create VIA_KEEPOUT object" ) ); } op->SetUnit( unit ); op->readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { op->AddComment( *its ); ++its; } } olnViaKeepout.push_back( op ); return; } if( CompareToken( ".PLACE_KEEPOUT", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".PLACE_KEEPOUT" ) ); PLACE_KO_OUTLINE* op = nullptr; try { op = new PLACE_KO_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not create PLACE_KEEPOUT object" ) ); } op->SetUnit( unit ); op->readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { op->AddComment( *its ); ++its; } } olnPlaceKeepout.push_back( op ); return; } if( CompareToken( ".PLACE_REGION", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".PLACE_REGION" ) ); GROUP_OUTLINE* op = nullptr; try { op = new GROUP_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "could not create PLACE_REGION object" ) ); } op->SetUnit( unit ); op->readData( aBoardFile, iline, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { op->AddComment( *its ); ++its; } } olnGroup.insert( pair(op->GetGroupName(), op) ); return; } if( CompareToken( ".DRILLED_HOLES", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".DRILLED_HOLES" ) ); readBrdDrills( aBoardFile, aBoardState ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { drillComments.push_back( *its ); ++its; } } return; } if( CompareToken( ".NOTES", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".NOTES" ) ); if( idfVer < IDF_V3 ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDFv2 file\n" "* Violation of specification: NOTES section not in " "specification" ) ); readBrdNotes( aBoardFile, aBoardState ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { noteComments.push_back( *its ); ++its; } } return; } if( CompareToken( ".PLACEMENT", token ) ) { if( aBoardState != IDF3::FILE_OUTLINE ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: expecting .BOARD_OUTLINE, have " ".PLACEMENT" ) ); readBrdPlacement( aBoardFile, aBoardState, aNoSubstituteOutlines ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { placeComments.push_back( *its ); ++its; } } return; } } } void IDF3_BOARD::readBoardFile( const std::string& aFileName, bool aNoSubstituteOutlines ) { OPEN_ISTREAM( brd, aFileName.c_str() ); try { brd.exceptions ( std::ios_base::badbit ); if( brd.fail() ) { ostringstream ostr; ostr << "\n* could not open file: '" << aFileName << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } brd.imbue( std::locale( "C" ) ); std::string iline; // the input line bool isComment; // true if a line just read in is a comment line std::streampos pos; IDF3::FILE_STATE state = IDF3::FILE_START; // note: as per IDFv3 specification: // "The Header section must be the first section in the file, the second // section must be the Outline section, and the last section must be the // Placement section. All other sections may be in any order." // further notes: Except for the HEADER section, sections may be preceded by // comment lines which will be copied back out on write(). No comments may // be associated with the board file itself since the only logical location // for unambiguous association is at the end of the file, which is inconvenient // for large files. readBrdHeader( brd, state ); // read the various sections while( state != IDF3::FILE_PLACEMENT && brd.good() ) readBrdSection( brd, state, aNoSubstituteOutlines ); if( !brd.good() ) { // check if we have valid data if( brd.eof() && state >= IDF3::FILE_OUTLINE && state < IDF3::FILE_INVALID ) { CLOSE_STREAM( brd ); return; } CLOSE_STREAM( brd ); ostringstream ostr; ostr << "\n* empty IDF file: '" << aFileName << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( brd.good() && state == IDF3::FILE_PLACEMENT ) { // read in any trailing lines and report on ignored comments (minor fault) // and any non-comment item (non-compliance with IDFv3) while( brd.good() ) { while( !FetchIDFLine( brd, iline, isComment, pos ) && brd.good() ); // normally this is a fault but we have all the data in accordance with specs if( ( !brd.good() && !brd.eof() ) || iline.empty() ) break; if( isComment ) { ERROR_IDF << "[warning]: trailing comments after PLACEMENT\n"; } else { ostringstream ostr; ostr << "\n* problems reading file: '" << aFileName << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF file\n" "* Violation of specification: non-comment lines after " "PLACEMENT section" ) ); } } } } catch( const std::exception& ) { brd.exceptions ( std::ios_base::goodbit ); CLOSE_STREAM( brd ); throw; } CLOSE_STREAM( brd ); return; } void IDF3_BOARD::readLibSection( std::istream& aLibFile, IDF3::FILE_STATE& aLibState, IDF3_BOARD* aBoard ) { if( aBoard == nullptr ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* BUG: invoked with nullptr reference aBoard" ) ); } std::list< std::string > comments; // comments associated with a section // Reads in .ELECTRICAL, .MECHANICAL or #COMMENTS std::string iline; // the input line bool isComment; // true if a line just read in is a comment line std::streampos pos; int idx = 0; bool quoted = false; std::string token; IDF3_COMP_OUTLINE* pout = nullptr; try { pout = new IDF3_COMP_OUTLINE( this ); } catch( std::bad_alloc& ) { throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* memory allocation failure" ) ); } while( aLibFile.good() ) { while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() ); if( !aLibFile.good() && !aLibFile.eof() ) { delete pout; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading library section" ) ); } // no data was read; this only happens at eof() if( iline.empty() ) { delete pout; return; } if( isComment ) { comments.push_back( iline ); continue; } // This must be a header GetIDFString( iline, token, quoted, idx ); if( quoted ) { ostringstream ostr; ostr << "invalid IDF library\n"; ostr << "* Violation of specification: quoted string where .ELECTRICAL or " ".MECHANICAL expected\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* pos: " << pos; delete pout; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( CompareToken( ".ELECTRICAL", token ) || CompareToken( ".MECHANICAL", token ) ) { pout->readData( aLibFile, token, idfVer ); if( !comments.empty() ) { std::list::iterator its = comments.begin(); std::list::iterator ite = comments.end(); while( its != ite ) { pout->AddComment( *its ); ++its; } } IDF3_COMP_OUTLINE* cop = aBoard->GetComponentOutline( pout->GetUID() ); if( cop == nullptr ) { compOutlines.insert( pair( pout->GetUID(), pout ) ); } else { if( MatchCompOutline( pout, cop ) ) { delete pout; // everything is fine; the outlines are genuine duplicates return; } ostringstream ostr; ostr << "invalid IDF library\n"; ostr << "duplicate Component Outline: '" << pout->GetUID() << "'\n"; ostr << "* Violation of specification: multiple outlines have the same GEOM and " "PART name\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* pos: " << pos; delete pout; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } return; } else { ostringstream ostr; ostr << "invalid IDF library\n"; ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << token << "'\n"; ostr << "* line: '" << iline << "'\n"; ostr << "* pos: " << pos; delete pout; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } } delete pout; if( !aLibFile.eof() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading IDF library file" ) ); return; } // read the library HEADER void IDF3_BOARD::readLibHeader( std::istream& aLibFile, IDF3::FILE_STATE& aLibState ) { std::string iline; // the input line bool isComment; // true if a line just read in is a comment line std::streampos pos; int idx = 0; bool quoted = false; std::string token; // RECORD 1: ".HEADER" must be the very first line while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() ); if( !aLibFile.good() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* premature end of file (no HEADER)" ) ); if( isComment ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification: first line must be .HEADER" ) ); if( !CompareToken( ".HEADER", iline ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification:\n" "* first line must be .HEADER and have no quotes or trailing text" ) ); // RECORD 2: // File Type [str]: LIBRARY_FILE // IDF Version Number [float]: must be 3.0 // Source System [str]: ignored // Date [str]: ignored // Library File Version [int]: ignored while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() ); if( !aLibFile.good() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* premature end of HEADER" ) ); if( isComment ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification: comment within .HEADER section" ) ); idx = 0; GetIDFString( iline, token, quoted, idx ); if( quoted ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification:\n" "* file Type in HEADER section must not be in quotes" ) ); if( !CompareToken( "LIBRARY_FILE", token ) ) { ostringstream ostr; ostr << "invalid IDF library\n"; ostr << "* Expecting string: LIBRARY_FILE (got '" << token << "')\n"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) ); if( quoted ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification: IDF Version must not be in quotes" ) ); if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) ) { idfVer = IDF_V3; } else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) ) { idfVer = IDF_V2; } else { ostringstream ostr; ostr << "unsupported IDF version\n"; ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) ); libSource = token; if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 4: no Date string" ) ); libDate = token; if( !GetIDFString( iline, token, quoted, idx ) ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) ); std::istringstream istr; istr.str( token ); istr >> libFileVersion; if( istr.fail() ) { ERROR_IDF << "invalid Library File Version in header\n"; cerr << "* Setting default version of 1\n"; libFileVersion = 1; } if( quoted ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification:\n" "* HEADER section, RECORD 2, FIELD 5: Library File Version must not " "be in quotes" ) ); // RECORD 3: // .END_HEADER while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() ); if( ( !aLibFile.good() && !aLibFile.eof() ) || iline.empty() ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading library header, RECORD 3" ) ); if( isComment ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid IDF library file\n" "* Violation of specification: comment within .HEADER section" ) ); if( !CompareToken( ".END_HEADER", iline ) ) { ostringstream ostr; ostr << "invalid IDF header\n"; ostr << "* Violation of specification: expected .END_HEADER (got '" << iline << "')\n"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } aLibState = IDF3::FILE_HEADER; } void IDF3_BOARD::readLibFile( const std::string& aFileName ) { OPEN_ISTREAM( lib, aFileName.c_str() ); try { lib.exceptions ( std::ios_base::badbit ); if( lib.fail() ) { ostringstream ostr; ostr << "\n* could not open file: '" << aFileName << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } lib.imbue( std::locale( "C" ) ); IDF3::FILE_STATE state = IDF3::FILE_START; readLibHeader( lib, state ); while( lib.good() ) readLibSection( lib, state, this ); } catch( const std::exception& ) { lib.exceptions ( std::ios_base::goodbit ); CLOSE_STREAM( lib ); throw; } CLOSE_STREAM( lib ); } bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutlines ) { // 1. Check that the file extension is 'emn' // 2. Check if a file with extension 'emp' exists and read it // 3. Open the specified filename and read it wxFileName brdname( aFullFileName ); wxFileName libname( aFullFileName ); wxString ext = brdname.GetExt(); if( !ext.Cmp( wxT( "EMN" ) ) ) { libname.SetExt( wxT( "EMP" ) ); } else if( !ext.Cmp( wxT( "emn" ) ) ) { libname.SetExt( wxT( "emp" ) ); } else { ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << "* [INFO] invalid file name: '" << aFullFileName.ToUTF8() << "'"; errormsg = ostr.str(); } brdname.SetExt( wxT( "emn" ) ); std::string bfname = TO_UTF8( aFullFileName ); if( !wxFileExists( bfname ) ) { brdname.SetExt( wxT( "EMN" ) ); libname.SetExt( wxT( "EMP" ) ); } try { if( !brdname.IsOk() ) { ostringstream ostr; ostr << "\n* invalid file name: '" << aFullFileName.ToUTF8() << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !brdname.FileExists() ) { ostringstream ostr; ostr << "\n* no such file: '" << aFullFileName.ToUTF8() << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( !brdname.IsFileReadable() ) { ostringstream ostr; ostr << "\n* cannot read file: '" << bfname << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } bfname = TO_UTF8( brdname.GetFullPath() ); std::string lfname = TO_UTF8( libname.GetFullPath() ); if( !libname.FileExists() ) { // NOTE: Since this is a common case we simply proceed // with the assumption that there is no library file; // however we print a message to inform the user. ERROR_IDF; cerr << "no associated library file (*.emp)\n"; } else if( !libname.IsFileReadable() ) { ostringstream ostr; ostr << "\n* cannot read library file: '" << lfname << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } else { // read the library file before proceeding readLibFile( lfname ); } // read the board file readBoardFile( bfname, aNoSubstituteOutlines ); } catch( const std::exception& e ) { Clear(); errormsg = e.what(); return false; } return true; } bool IDF3_BOARD::writeLibFile( const std::string& aFileName ) { OPEN_OSTREAM( lib, aFileName.c_str() ); try { lib.exceptions( std::ios_base::failbit ); if( lib.fail() ) { ostringstream ostr; ostr << "\n* could not open file: '" << aFileName << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } lib.imbue( std::locale( "C" ) ); wxDateTime tdate( time( nullptr ) ); if( idfSource.empty() ) idfSource = "KiCad-IDF Framework"; ostringstream fileDate; fileDate << setfill( '0' ) << setw(4) << tdate.GetYear(); fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay(); fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond(); libDate = fileDate.str(); lib << ".HEADER\n"; lib << "LIBRARY_FILE 3.0 \"Created by " << idfSource; lib << "\" " << libDate << " " << (++libFileVersion) << "\n"; lib << ".END_HEADER\n\n"; std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin(); std::map< std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end(); while( its != ite ) { its->second->writeData( lib ); ++its; } } catch( const std::exception& ) { lib.exceptions( std::ios_base::goodbit ); CLOSE_STREAM( lib ); throw; } CLOSE_STREAM( lib ); return true; } void IDF3_BOARD::writeBoardFile( const std::string& aFileName ) { OPEN_OSTREAM( brd, aFileName.c_str() ); try { brd.exceptions( std::ostream::failbit ); if( brd.fail() ) { ostringstream ostr; ostr << "\n* could not open file: '" << aFileName << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } brd.imbue( std::locale( "C" ) ); wxDateTime tdate( time( nullptr ) ); if( idfSource.empty() ) idfSource = "KiCad-IDF Framework"; ostringstream fileDate; fileDate << setfill( '0' ) << setw(4) << tdate.GetYear(); fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay(); fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond(); brdDate = fileDate.str(); brd << ".HEADER\n"; brd << "BOARD_FILE 3.0 \"Created by " << idfSource; brd << "\" " << brdDate << " " << (++brdFileVersion) << "\n"; if( boardName.empty() ) brd << "\"BOARD WITH NO NAME\" "; else brd << "\"" << boardName << "\" "; brd << setw(1) << setfill( ' ' ); if( unit == IDF3::UNIT_MM ) brd << "MM\n"; else brd << "THOU\n"; brd << ".END_HEADER\n\n"; // write the BOARD_OUTLINE olnBoard.writeData( brd ); // OTHER outlines do { std::map::iterator its = olnOther.begin(); std::map::iterator ite = olnOther.end(); while(its != ite ) { its->second->writeData( brd ); ++its; } } while( 0 ); // ROUTE outlines do { std::list::iterator its = olnRoute.begin(); std::list::iterator ite = olnRoute.end(); while( its != ite ) { (*its)->writeData( brd ); ++its; } } while( 0 ); // PLACEMENT outlines do { std::list::iterator its = olnPlace.begin(); std::list::iterator ite = olnPlace.end(); while( its != ite ) { (*its)->writeData( brd ); ++its; } } while( 0 ); // ROUTE KEEPOUT outlines do { std::list::iterator its = olnRouteKeepout.begin(); std::list::iterator ite = olnRouteKeepout.end(); while( its != ite ) { (*its)->writeData( brd ); ++its; } } while( 0 ); // VIA KEEPOUT outlines do { std::list::iterator its = olnViaKeepout.begin(); std::list::iterator ite = olnViaKeepout.end(); while( its != ite ) { (*its)->writeData( brd ); ++its; } } while( 0 ); // PLACE KEEPOUT outlines do { std::list::iterator its = olnPlaceKeepout.begin(); std::list::iterator ite = olnPlaceKeepout.end(); while( its != ite ) { (*its)->writeData( brd ); ++its; } } while( 0 ); // PLACEMENT GROUP outlines do { std::multimap::iterator its = olnGroup.begin(); std::multimap::iterator ite = olnGroup.end(); while( its != ite ) { its->second->writeData( brd ); ++its; } } while( 0 ); // Drilled holes do { std::list::iterator itds = drillComments.begin(); std::list::iterator itde = drillComments.end(); while( itds != itde ) { brd << "# " << *itds << "\n"; ++itds; } brd << ".DRILLED_HOLES\n"; std::list::iterator itbs = board_drills.begin(); std::list::iterator itbe = board_drills.end(); while( itbs != itbe ) { (*itbs)->write( brd, unit ); ++itbs; } std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin(); std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end(); while( itcs != itce ) { itcs->second->writeDrillData( brd ); ++itcs; } brd << ".END_DRILLED_HOLES\n\n"; } while( 0 ); // Notes if( !notes.empty() ) { std::list::iterator itncs = noteComments.begin(); std::list::iterator itnce = noteComments.end(); while( itncs != itnce ) { brd << "# " << *itncs << "\n"; ++itncs; } brd << ".NOTES\n"; std::list::iterator itns = notes.begin(); std::list::iterator itne = notes.end(); while( itns != itne ) { (*itns)->writeNote( brd, unit ); ++itns; } brd << ".END_NOTES\n\n"; } // Placement if( !components.empty() ) { std::list::iterator itpcs = placeComments.begin(); std::list::iterator itpce = placeComments.end(); while( itpcs != itpce ) { brd << "# " << *itpcs << "\n"; ++itpcs; } std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin(); std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end(); // determine if there are any component outlines at all and avoid // writing an empty PLACEMENT section if there are no outlines. // this will cost a little time but prevents software such as // CircuitWorks from segfaulting on an empty section. bool hasOutlines = false; while( itcs != itce ) { if( itcs->second->GetOutlinesSize() > 0 ) { itcs = components.begin(); hasOutlines = true; break; } ++itcs; } if( hasOutlines ) { brd << ".PLACEMENT\n"; while( itcs != itce ) { itcs->second->writePlaceData( brd ); ++itcs; } brd << ".END_PLACEMENT\n"; } } } catch( const std::exception& ) { brd.exceptions( std::ios_base::goodbit ); CLOSE_STREAM( brd ); throw; } CLOSE_STREAM( brd ); return; } bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aForceUnitFlag ) { if( aUnitMM != IDF3::UNIT_THOU ) setUnit( IDF3::UNIT_MM, aForceUnitFlag ); else setUnit( IDF3::UNIT_THOU, aForceUnitFlag ); // 1. Check that the file extension is 'emn' // 2. Write the *.emn file according to the IDFv3 spec // 3. Write the *.emp file according to the IDFv3 spec wxFileName brdname( aFullFileName ); wxFileName libname( aFullFileName ); brdname.SetExt( wxT( "emn" ) ); libname.SetExt( wxT( "emp" ) ); std::string bfname = TO_UTF8( aFullFileName ); try { if( !brdname.IsOk() ) { ostringstream ostr; ostr << "\n* invalid file name: '" << bfname << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } if( brdname.FileExists() && !brdname.IsFileWritable() ) { ostringstream ostr; ostr << "cannot overwrite existing board file\n"; ostr << "* filename: '" << bfname << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } bfname = TO_UTF8( brdname.GetFullPath() ); std::string lfname = TO_UTF8( libname.GetFullPath() ); if( libname.FileExists() && !libname.IsFileWritable() ) { ostringstream ostr; ostr << "cannot overwrite existing library file\n"; ostr << "* filename: '" << lfname << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } writeLibFile( lfname ); writeBoardFile( bfname ); } catch( const std::exception& e ) { errormsg = e.what(); return false; } return true; } const std::string& IDF3_BOARD::GetIDFSource( void ) { return idfSource; } void IDF3_BOARD::SetIDFSource( const std::string& aIDFSource ) { idfSource = aIDFSource; return; } const std::string& IDF3_BOARD::GetBoardSource( void ) { return brdSource; } const std::string& IDF3_BOARD::GetLibrarySource( void ) { return libSource; } const std::string& IDF3_BOARD::GetBoardDate( void ) { return brdDate; } const std::string& IDF3_BOARD::GetLibraryDate( void ) { return libDate; } int IDF3_BOARD::GetBoardVersion( void ) { return brdFileVersion; } bool IDF3_BOARD::SetBoardVersion( int aVersion ) { if( aVersion < 0 ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* board version (" << aVersion << ") must be >= 0"; errormsg = ostr.str(); return false; } brdFileVersion = aVersion; return true; } int IDF3_BOARD::GetLibraryVersion( void ) { return libFileVersion; } bool IDF3_BOARD::SetLibraryVersion( int aVersion ) { if( aVersion < 0 ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* library version (" << aVersion << ") must be >= 0"; errormsg = ostr.str(); return false; } libFileVersion = aVersion; return true; } double IDF3_BOARD::GetUserScale( void ) { return userScale; } bool IDF3_BOARD::SetUserScale( double aScaleFactor ) { if( aScaleFactor == 0.0 ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* BUG: user scale factor must not be 0"; errormsg = ostr.str(); return false; } userScale = aScaleFactor; return true; } int IDF3_BOARD::GetUserPrecision( void ) { return userPrec; } bool IDF3_BOARD::SetUserPrecision( int aPrecision ) { if( aPrecision < 1 || aPrecision > 8 ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* precision value (" << aPrecision << ") must be 1..8"; errormsg = ostr.str(); return false; } userPrec = aPrecision; return true; } void IDF3_BOARD::GetUserOffset( double& aXoff, double& aYoff ) { aXoff = userXoff; aYoff = userYoff; } void IDF3_BOARD::SetUserOffset( double aXoff, double aYoff ) { userXoff = aXoff; userYoff = aYoff; } bool IDF3_BOARD::AddBoardOutline( IDF_OUTLINE* aOutline ) { if( !olnBoard.AddOutline( aOutline ) ) { errormsg = olnBoard.GetError(); return false; } return true; } bool IDF3_BOARD::DelBoardOutline( IDF_OUTLINE* aOutline ) { if( !olnBoard.DelOutline( aOutline ) ) { errormsg = olnBoard.GetError(); return false; } return true; } bool IDF3_BOARD::DelBoardOutline( size_t aIndex ) { if( !olnBoard.DelOutline( aIndex ) ) { errormsg = olnBoard.GetError(); return false; } return true; } size_t IDF3_BOARD::GetBoardOutlinesSize( void ) { return olnBoard.OutlinesSize(); } BOARD_OUTLINE* IDF3_BOARD::GetBoardOutline( void ) { return &olnBoard; } const std::list< IDF_OUTLINE* >*const IDF3_BOARD::GetBoardOutlines( void ) { return olnBoard.GetOutlines(); } IDF_DRILL_DATA* IDF3_BOARD::AddBoardDrill( double aDia, double aXpos, double aYpos, IDF3::KEY_PLATING aPlating, const std::string& aHoleType, IDF3::KEY_OWNER aOwner ) { IDF_DRILL_DATA* drill = nullptr; try { drill = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating, "BOARD", aHoleType, aOwner ); } catch( std::bad_alloc& ) { return nullptr; } board_drills.push_back( drill ); return drill; } IDF_DRILL_DATA* IDF3_BOARD::AddDrill( IDF_DRILL_DATA* aDrilledHole ) { if( !aDrilledHole ) return nullptr; // note: PANEL drills are essentially BOARD drills which // the panel requires to be present if( CompareToken( "BOARD", aDrilledHole->GetDrillRefDes() ) || CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) ) { board_drills.push_back( aDrilledHole ); return aDrilledHole; } return addCompDrill( aDrilledHole ); } bool IDF3_BOARD::DelBoardDrill( double aDia, double aXpos, double aYpos ) { errormsg.clear(); std::list::iterator sp = board_drills.begin(); std::list::iterator ep = board_drills.end(); bool rval = false; while( sp != ep ) { if( (*sp)->Matches( aDia, aXpos, aYpos ) ) { #ifndef DISABLE_IDF_OWNERSHIP IDF3::KEY_OWNER keyo = (*sp)->GetDrillOwner(); if( keyo == UNOWNED || ( keyo == MCAD && cadType == CAD_MECH ) || ( keyo == ECAD && cadType == CAD_ELEC ) ) { rval = true; delete *sp; sp = board_drills.erase( sp ); continue; } else { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* ownership violation; drill owner ("; switch( keyo ) { case ECAD: ostr << "ECAD"; break; case MCAD: ostr << "MCAD"; break; default: ostr << "invalid: " << keyo; break; } ostr << ") may not be modified by "; if( cadType == CAD_MECH ) ostr << "MCAD"; else ostr << "ECAD"; errormsg = ostr.str(); ++sp; continue; } #else rval = true; delete *sp; sp = board_drills.erase( sp ); continue; #endif } ++sp; } return rval; } bool IDF3_BOARD::AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY ) { if( aWidth < IDF_MIN_DIA_MM ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* slot width (" << aWidth << ") must be >= " << IDF_MIN_DIA_MM; errormsg = ostr.str(); return false; } if( aLength < IDF_MIN_DIA_MM ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* slot length (" << aLength << ") must be >= " << IDF_MIN_DIA_MM; errormsg = ostr.str(); return false; } IDF_POINT c[2]; // centers IDF_POINT pt[4]; double a1 = aOrientation / 180.0 * M_PI; double a2 = a1 + M_PI_2; double d1 = aLength / 2.0; double d2 = aWidth / 2.0; double sa1 = sin( a1 ); double ca1 = cos( a1 ); double dsa2 = d2 * sin( a2 ); double dca2 = d2 * cos( a2 ); c[0].x = aX + d1 * ca1; c[0].y = aY + d1 * sa1; c[1].x = aX - d1 * ca1; c[1].y = aY - d1 * sa1; pt[0].x = c[0].x - dca2; pt[0].y = c[0].y - dsa2; pt[1].x = c[1].x - dca2; pt[1].y = c[1].y - dsa2; pt[2].x = c[1].x + dca2; pt[2].y = c[1].y + dsa2; pt[3].x = c[0].x + dca2; pt[3].y = c[0].y + dsa2; IDF_OUTLINE* outline = nullptr; try { outline = new IDF_OUTLINE; } catch( std::bad_alloc& ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* could not create an outline object"; errormsg = ostr.str(); return false; } // first straight run IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] ); outline->push( seg ); // first 180 degree cap seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true ); outline->push( seg ); // final straight run seg = new IDF_SEGMENT( pt[2], pt[3] ); outline->push( seg ); // final 180 degree cap seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true ); outline->push( seg ); if( !olnBoard.addOutline( outline ) ) { errormsg = olnBoard.GetError(); return false; } return true; } IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( double aDia, double aXpos, double aYpos, IDF3::KEY_PLATING aPlating, const std::string& aHoleType, IDF3::KEY_OWNER aOwner, const std::string& aRefDes ) { // first find the matching component; if it doesn't exist we must create it somehow - // question is, do we need a component outline at this stage or can those be added later? // // Presumably we can create a component with no outline and add the outlines later. // If a component is created and an outline specified but the outline is not loaded, // we're screwed if (a) we have already read the library file (*.emp) or (b) we don't // know the filename std::string refdes = aRefDes; // note: for components 'NOREFDES' would be assigned a Unique ID, but for holes // there is no way of associating the hole with the correct entity (if any) // so a hole added with "NOREFDES" goes to a generic component "NOREFDES" if( refdes.empty() ) refdes = "NOREFDES"; // check if the target is BOARD or PANEL if( CompareToken( "BOARD", refdes ) ) return AddBoardDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner ); if( CompareToken( "PANEL", refdes ) ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* PANEL data not supported"; errormsg = ostr.str(); return nullptr; } std::map::iterator ref = components.find( refdes ); if( ref == components.end() ) { // create the item IDF3_COMPONENT* comp = nullptr; try { comp = new IDF3_COMPONENT( this ); } catch( std::bad_alloc& ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* could not create new component object"; errormsg = ostr.str(); return nullptr; } comp->SetParent( this ); comp->SetRefDes( refdes ); ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first; } // add the drill IDF_DRILL_DATA* dp = ref->second->AddDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner ); if( !dp ) { errormsg = ref->second->GetError(); return nullptr; } return dp; } IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( IDF_DRILL_DATA* aDrilledHole ) { if( !aDrilledHole ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): nullptr pointer"; errormsg = ostr.str(); return nullptr; } if( CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* PANEL data not supported"; errormsg = ostr.str(); return nullptr; } std::map::iterator ref = components.find( aDrilledHole->GetDrillRefDes() ); if( ref == components.end() ) { // create the item IDF3_COMPONENT* comp; try { comp = new IDF3_COMPONENT( this ); } catch( std::bad_alloc& ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* could not create new component object"; errormsg = ostr.str(); return nullptr; } comp->SetParent( this ); comp->SetRefDes( aDrilledHole->GetDrillRefDes() ); ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first; } IDF_DRILL_DATA* dp = ref->second->AddDrill( aDrilledHole ); if( !dp ) { errormsg = ref->second->GetError(); return nullptr; } return dp; } bool IDF3_BOARD::delCompDrill( double aDia, double aXpos, double aYpos, const std::string& aRefDes ) { errormsg.clear(); std::map::iterator ref = components.find( aRefDes ); if( ref == components.end() ) return false; if( !ref->second->DelDrill( aDia, aXpos, aYpos ) ) { errormsg = ref->second->GetError(); return false; } return true; } bool IDF3_BOARD::AddComponent( IDF3_COMPONENT* aComponent ) { if( !aComponent ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__; ostr << "(): Invalid component pointer (nullptr)"; errormsg = ostr.str(); return false; } if( components.insert( std::pair ( aComponent->GetRefDes(), aComponent ) ).second == false ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n"; ostr << "* duplicate RefDes ('" << aComponent->GetRefDes() << "')"; errormsg = ostr.str(); return false; } return true; } bool IDF3_BOARD::DelComponent( IDF3_COMPONENT* aComponent ) { errormsg.clear(); #ifndef DISABLE_IDF_OWNERSHIP if( !checkComponentOwnership( __LINE__, __FUNCTION__, aComponent ) ) return false; #endif std::map::iterator it = components.find( aComponent->GetRefDes() ); if( it == components.end() ) return false; delete it->second; components.erase( it ); return true; } bool IDF3_BOARD::DelComponent( size_t aIndex ) { if( aIndex >= components.size() ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n"; ostr << "* aIndex (" << aIndex << ") out of range (" << components.size() << ")"; errormsg = ostr.str(); return false; } std::map::iterator it = components.begin(); while( aIndex-- > 0 ) ++it; #ifndef DISABLE_IDF_OWNERSHIP if( !checkComponentOwnership( __LINE__, __FUNCTION__, it->second ) ) return false; #endif delete it->second; components.erase( it ); return true; } size_t IDF3_BOARD::GetComponentsSize( void ) { return components.size(); } std::map< std::string, IDF3_COMPONENT* >*const IDF3_BOARD::GetComponents( void ) { return &components; } IDF3_COMPONENT* IDF3_BOARD::FindComponent( const std::string& aRefDes ) { std::map::iterator it = components.find( aRefDes ); if( it == components.end() ) return nullptr; return it->second; } IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const wxString& aFullFileName ) { std::string fname = TO_UTF8( aFullFileName ); wxFileName idflib( aFullFileName ); if( !idflib.IsOk() ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n"; cerr << "* invalid file name: '" << fname << "'"; errormsg = ostr.str(); return nullptr; } if( !idflib.FileExists() ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n"; cerr << "* no such file: '" << fname << "'"; errormsg = ostr.str(); return nullptr; } if( !idflib.IsFileReadable() ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n"; cerr << "* cannot read file: '" << fname << "'"; errormsg = ostr.str(); return nullptr; } std::map< std::string, std::string >::iterator itm = uidFileList.find( fname ); if( itm != uidFileList.end() ) return GetComponentOutline( itm->second ); IDF3_COMP_OUTLINE* cp = nullptr; try { cp = new IDF3_COMP_OUTLINE( this ); } catch( std::bad_alloc& ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n"; cerr << "* failed to create outline\n"; cerr << "* filename: '" << fname << "'"; errormsg = ostr.str(); return nullptr; } OPEN_ISTREAM( model, fname.c_str() ); try { model.exceptions ( std::ios_base::badbit ); if( model.fail() ) { ostringstream ostr; ostr << "\n* could not open file: '" << fname << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } model.imbue( std::locale( "C" ) ); std::string iline; // the input line bool isComment; // true if a line just read in is a comment line std::streampos pos; while( true ) { while( !FetchIDFLine( model, iline, isComment, pos ) && model.good() ); if( !model.good() ) { ostringstream ostr; ostr << "\n* problems reading file: '" << fname << "'"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } // accept comment lines, .ELECTRICAL, or .MECHANICAL only if( isComment ) { cp->AddComment( iline ); continue; } if( CompareToken( ".ELECTRICAL", iline ) || CompareToken( ".MECHANICAL", iline ) ) { cp->readData( model, iline, idfVer ); break; } else { ostringstream ostr; ostr << "faulty IDF component definition\n"; ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << iline << "'\n"; cerr << "* File: '" << fname << "'\n"; throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) ); } } } catch( const std::exception& e ) { delete cp; model.exceptions ( std::ios_base::goodbit ); CLOSE_STREAM( model ); errormsg = e.what(); return nullptr; } CLOSE_STREAM( model ); // check the unique ID against the list from library components std::list< std::string >::iterator lsts = uidLibList.begin(); std::list< std::string >::iterator lste = uidLibList.end(); std::string uid = cp->GetUID(); IDF3_COMP_OUTLINE* oldp = nullptr; while( lsts != lste ) { if( ! lsts->compare( uid ) ) { oldp = GetComponentOutline( uid ); if( MatchCompOutline( cp, oldp ) ) { // everything is fine; the outlines are genuine duplicates; delete the copy delete cp; // make sure we can find the item via its filename uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) ); // return the pointer to the original return oldp; } else { delete cp; ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n"; ostr << "* original loaded from library, duplicate in current file\n"; ostr << "* file: '" << fname << "'"; errormsg = ostr.str(); return nullptr; } } ++lsts; } // if we got this far then any duplicates are from files previously read oldp = GetComponentOutline( uid ); if( oldp == nullptr ) { // everything is fine, there are no existing entries uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) ); compOutlines.insert( pair( uid, cp ) ); return cp; } if( MatchCompOutline( cp, oldp ) ) { // everything is fine; the outlines are genuine duplicates; delete the copy delete cp; // make sure we can find the item via its other filename uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) ); // return the pointer to the original return oldp; } delete cp; // determine the file name of the first instance std::map< std::string, std::string >::iterator ufls = uidFileList.begin(); std::map< std::string, std::string >::iterator ufle = uidFileList.end(); std::string oldfname; while( ufls != ufle ) { if( ! ufls->second.compare( uid ) ) { oldfname = ufls->first; break; } ++ufls; } ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n"; ostr << "* original file: '" << oldfname << "'\n"; ostr << "* this file: '" << fname << "'"; errormsg = ostr.str(); return nullptr; } IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string& aComponentID ) { std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.find( aComponentID ); if( its != compOutlines.end() ) return its->second; return nullptr; } IDF3_COMP_OUTLINE* IDF3_BOARD::GetInvalidOutline( const std::string& aGeomName, const std::string& aPartName ) { std::string uid; bool empty = false; if( aGeomName.empty() && aPartName.empty() ) { uid = "NOGEOM_NOPART"; empty = true; } else { uid = aGeomName + "_" + aPartName; } IDF3_COMP_OUTLINE* cp = GetComponentOutline( uid ); if( cp != nullptr ) return cp; try { cp = new IDF3_COMP_OUTLINE( this ); } catch( std::bad_alloc& ) { ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "; cerr << "could not create new outline"; errormsg = ostr.str(); return nullptr; } if( empty ) cp->CreateDefaultOutline( "", "" ); else cp->CreateDefaultOutline( aGeomName, aPartName ); compOutlines.insert( pair(cp->GetUID(), cp) ); return cp; } void IDF3_BOARD::Clear( void ) { // preserve the board thickness double thickness = olnBoard.GetThickness(); idfSource.clear(); brdSource.clear(); libSource.clear(); brdDate.clear(); libDate.clear(); uidFileList.clear(); uidLibList.clear(); brdFileVersion = 0; libFileVersion = 0; iRefDes = 0; sRefDes.clear(); // delete comment lists noteComments.clear(); drillComments.clear(); placeComments.clear(); // delete notes while( !notes.empty() ) { delete notes.front(); notes.pop_front(); } // delete drill list do { std::list::iterator ds = board_drills.begin(); std::list::iterator de = board_drills.end(); while( ds != de ) { delete *ds; ++ds; } board_drills.clear(); } while( 0 ); // delete components do { std::map::iterator cs = components.begin(); std::map::iterator ce = components.end(); while( cs != ce ) { delete cs->second; ++cs; } components.clear(); } while( 0 ); // delete component outlines do { std::map::iterator cs = compOutlines.begin(); std::map::iterator ce = compOutlines.end(); while( cs != ce ) { delete cs->second; ++cs; } compOutlines.clear(); } while( 0 ); // delete OTHER outlines do { std::map::iterator os = olnOther.begin(); std::map::iterator oe = olnOther.end(); while( os != oe ) { delete os->second; ++os; } olnOther.clear(); } while( 0 ); // delete ROUTE outlines do { std::list::iterator os = olnRoute.begin(); std::list::iterator oe = olnRoute.end(); while( os != oe ) { delete *os; ++os; } olnRoute.clear(); } while( 0 ); // delete PLACE outlines do { std::list::iterator os = olnPlace.begin(); std::list::iterator oe = olnPlace.end(); while( os != oe ) { delete *os; ++os; } olnPlace.clear(); } while( 0 ); // delete ROUTE KEEPOUT outlines do { std::list::iterator os = olnRouteKeepout.begin(); std::list::iterator oe = olnRouteKeepout.end(); while( os != oe ) { delete *os; ++os; } olnRouteKeepout.clear(); } while( 0 ); // delete VIA KEEPOUT outlines do { std::list::iterator os = olnViaKeepout.begin(); std::list::iterator oe = olnViaKeepout.end(); while( os != oe ) { delete *os; ++os; } olnViaKeepout.clear(); } while( 0 ); // delete PLACEMENT KEEPOUT outlines do { std::list::iterator os = olnPlaceKeepout.begin(); std::list::iterator oe = olnPlaceKeepout.end(); while( os != oe ) { delete *os; ++os; } olnPlaceKeepout.clear(); } while( 0 ); // delete PLACEMENT GROUP outlines do { std::multimap::iterator os = olnGroup.begin(); std::multimap::iterator oe = olnGroup.end(); while( os != oe ) { delete os->second; ++os; } olnGroup.clear(); } while( 0 ); boardName.clear(); olnBoard.setThickness( thickness ); unit = UNIT_MM; userScale = 1.0; userXoff = 0.0; userYoff = 0.0; } const std::map* const IDF3_BOARD::GetOtherOutlines( void ) { return &olnOther; }