//---------------------------------------------------------------------------- // EgalTech 2019-2023 //---------------------------------------------------------------------------- // File : AutoNester.cpp Data : 17.07.23 Versione : 2.5g2 // Contenuto : Implementazione della classe AutoNester. // // // // Modifiche : 28.11.19 DS Creazione modulo. // 17.07.23 PS Compilato con Optalog ver. 9.0. // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "AutoNester.h" #include "DllMain.h" #include "/EgtDev/Include/ENsDllMain.h" #include "/EgtDev/Include/EGkPolyArc.h" #include "/EgtDev/Extern/Optalog/Include/cns.h" #include "/EgtDev/Extern/Optalog/Include/cns_tooling.h" #include "/EgtDev/Extern/Optalog/Include/cns_restricted_zone.h" #include "/EgtDev/Extern/Optalog/Include/cns_textile.h" #include "/EgtDev/Extern/Optalog/Include/cns_shear_sequence.h" #include "/EgtDev/Extern/Optalog/Include/cns_egaltech.h" #include "/EgtDev/Include/SELkLockId.h" using namespace std ; //---------------------------------------------------------------------------- IAutoNester* CreateAutoNester( void) { // verifico la chiave e le opzioni if ( ! TestKeyForENs( GetENsKey(), 0, GetENsLogger())) return nullptr ; // creo il NestMgr return static_cast ( new(nothrow) AutoNester) ; } //---------------------------------------------------------------------------- static void GetCNSPath( const PolyArc& Outline, double dOffsX, double dOffsY, vector& vElem) { // converto nel formato Optalog int nElem = Outline.GetPointNbr() ; if ( Outline.IsClosed()) -- nElem ; vElem.reserve( nElem) ; Point3d ptIni, ptFin ; double dBulge ; bool bNext = Outline.GetFirstArc( ptIni, ptFin, dBulge) ; while ( bNext) { double dLeftDeflection = - dBulge * DistXY( ptIni, ptFin) / 2 ; vElem.push_back( { dOffsX + ptFin.x, dOffsY + ptFin.y, dLeftDeflection}) ; bNext = Outline.GetNextArc( ptIni, ptFin, dBulge) ; } } //---------------------------------------------------------------------------- // AutoNester //---------------------------------------------------------------------------- AutoNester::AutoNester( void) : m_pOrder( nullptr), m_pComp( nullptr), m_dInterpartGap( 0), m_dShearGap( 0), m_bGuillotine( false) { } //---------------------------------------------------------------------------- AutoNester::~AutoNester( void) { Clear() ; } //---------------------------------------------------------------------------- bool AutoNester::Clear( void) { if ( m_pOrder != nullptr) { if ( m_pComp != nullptr) { CNS_ComputationStatus status = CNS_GetComputationStatus( m_pComp) ; if ( status != CNS_Ok && status != CNS_CancelledComputation) CNS_CancelComputation( m_pComp) ; } CNS_DeleteLaunchingOrder( m_pOrder) ; m_pOrder = nullptr ; m_pComp = nullptr ; } m_IdSheetInfo.clear() ; m_IdRzCstrPtr.clear() ; m_IdPartPtr.clear() ; m_dInterpartGap = 0 ; m_dShearGap = 0 ; m_bGuillotine = false ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::Start( void) { Clear() ; m_pOrder = CNS_NewLaunchingOrder() ; return ( m_pOrder != nullptr) ; } //---------------------------------------------------------------------------- bool AutoNester::SetGuillotineMode( void) { if ( m_pOrder == nullptr) return false ; CNS_SetShearMode( m_pOrder, 1) ; m_bGuillotine = true ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::SetStartCorner( int nCorner) { if ( m_pOrder == nullptr) return false ; CNS_Origin cnsOrig ; switch ( nCorner) { case NST_CORNER_BL : cnsOrig = CNS_BottomLeft ; break ; case NST_CORNER_TL : cnsOrig = CNS_TopLeft ; break ; case NST_CORNER_BR : cnsOrig = CNS_BottomRight ; break ; case NST_CORNER_TR : cnsOrig = CNS_TopRight ; break ; default : return false ; } CNS_SetOrigin( m_pOrder, cnsOrig) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::AddSheet( int nSheetId, const PolyArc& Outline, double dKerf, int nPriority, int nCount, bool* pbIsRect) { if ( m_pOrder == nullptr) return false ; // verifico outline if ( Outline.GetArcNbr() == 0 || ! Outline.IsClosed()) return false ; CNS_SheetPtr pSheet = nullptr ; double dOffsX = 0 ; double dOffsY = 0 ; // se rettangolo BBox3d b3Rect ; if ( Outline.IsRectangleXY( b3Rect)) { // imposto il rettangolo Point3d ptMin ; double dDimX, dDimY, dDimZ ; b3Rect.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ; pSheet = CNS_AddSheet( m_pOrder, nCount, dDimX, dDimY) ; if ( pSheet == nullptr) return false ; // salvo offset per origine reale del rettangolo dOffsX = ptMin.x ; dOffsY = ptMin.y ; // imposto altri dati CNS_SetSheetGaps( pSheet, dKerf, dKerf, dKerf, dKerf) ; // se richiesto, ritorno che è un rettangolo if ( pbIsRect != nullptr) *pbIsRect = true ; } // altrimenti forma generica else { // aggiungo contorno vector vElem ; GetCNSPath( Outline, 0, 0, vElem) ; pSheet = CNS_AddNonRectangularSheet( m_pOrder, nCount, int( vElem.size()), vElem.data()) ; if ( pSheet == nullptr) return false ; // imposto altri dati CNS_SetNonRectangularSheetGaps( pSheet, dKerf, dKerf, dKerf, dKerf, dKerf) ; // se richiesto, ritorno che non è un rettangolo if ( pbIsRect != nullptr) *pbIsRect = false ; } // imposto altri dati CNS_SetSheetUserString( pSheet, ToString( nSheetId).c_str()) ; CNS_SetSheetPriority( pSheet, nPriority) ; // lo inserisco nel map m_IdSheetInfo.emplace( nSheetId, SheetInfo( pSheet, dOffsX, dOffsY, dKerf)) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::AddDefectToSheet( int nSheetId, const PolyArc& Outline) { if ( m_pOrder == nullptr) return false ; // verifico outline if ( Outline.GetArcNbr() == 0 || ! Outline.IsClosed()) return false ; // cerco lo sheet const auto Iter = m_IdSheetInfo.find( nSheetId) ; if ( Iter == m_IdSheetInfo.end()) return false ; CNS_SheetPtr pSheet = Iter->second.pSheet ; double dOffsX = Iter->second.dOffsX ; double dOffsY = Iter->second.dOffsY ; // aggiungo difetto tramite suo contorno vector vElem ; GetCNSPath( Outline, -dOffsX, -dOffsY, vElem) ; CNS_AddDefectToSheet( pSheet, int( vElem.size()), vElem.data()) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::AddRestrictedZoneToSheet( int nRzConstrId, int nSheetId, const PolyArc& Outline) { if ( m_pOrder == nullptr) return false ; // verifico outline if ( Outline.GetArcNbr() == 0 || ! Outline.IsClosed()) return false ; // cerco lo sheet const auto IterS = m_IdSheetInfo.find( nSheetId) ; if ( IterS == m_IdSheetInfo.end()) return false ; CNS_SheetPtr pSheet = IterS->second.pSheet ; double dOffsX = IterS->second.dOffsX ; double dOffsY = IterS->second.dOffsY ; // cerco il vincolo CNS_RestrictedZoneConstraintPtr pRzCstr = nullptr ; const auto IterC = m_IdRzCstrPtr.find( nRzConstrId) ; if ( IterC == m_IdRzCstrPtr.end()) { // creo vincolo per zona ristretta pRzCstr = CNS_CreateRestrictedZoneConstraint( m_pOrder) ; if ( pRzCstr == nullptr) return false ; m_IdRzCstrPtr.emplace( nRzConstrId, pRzCstr) ; } else pRzCstr = IterC->second ; // aggiungo zona ristretta tramite suo contorno BBox3d b3Rect ; if ( Outline.IsRectangleXY( b3Rect)) { // imposto il rettangolo Point3d ptMin, ptMax ; b3Rect.GetMinMax( ptMin, ptMax) ; CNS_Point CNSPT_Min, CNSPT_Max ; CNSPT_Min.x = ptMin.x - dOffsX ; CNSPT_Min.y = ptMin.y - dOffsY ; CNSPT_Max.x = ptMax.x - dOffsX ; CNSPT_Max.y = ptMax.y - dOffsY ; ptMin += Vector3d( -dOffsX, -dOffsY, 0) ; ptMax += Vector3d( -dOffsX, -dOffsY, 0) ; CNS_SheetAddBoxRestrictedZone( pRzCstr, pSheet, CNSPT_Min, CNSPT_Max) ; } else { vector vElem ; GetCNSPath( Outline, -dOffsX, -dOffsY, vElem) ; CNS_SheetAddRestrictedZone( pRzCstr, pSheet, int( vElem.size()), vElem.data()) ; } return true ; } //---------------------------------------------------------------------------- bool AutoNester::AddPart( int nPartId, const PolyArc& Outline, bool bCanFlip, bool bCanRotate, double dRotStep, int nPriority, int nCount) { if ( m_pOrder == nullptr) return false ; // verifico outline if ( Outline.GetArcNbr() == 0 || ! Outline.IsClosed()) return false ; // aggiungo contorno vector vElem ; GetCNSPath( Outline, 0, 0, vElem) ; if ( vElem.size() <= 1) return false ; else if ( vElem.size() == 2) { if ( abs( vElem[0].left_deflection) < 10 * EPS_SMALL && abs( vElem[1].left_deflection) < 10 * EPS_SMALL) return false ; } CNS_PartPtr pPart = CNS_AddPart( m_pOrder, nCount, int( vElem.size()), vElem.data()) ; if ( pPart == nullptr) return false ; // imposto altri dati CNS_SetPartUserString( pPart, ToString( nPartId).c_str()) ; CNS_SetPartAuthorizations( pPart, ( bCanFlip ? 1 : 0), ( bCanRotate ? 1 : 0), dRotStep) ; CNS_SetPartPriority( pPart, nPriority) ; // lo inserisco nel map m_IdPartPtr.emplace( nPartId, pPart) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::AddHoleToPart( int nPartId, const PolyArc& Hole) { if ( m_pOrder == nullptr) return false ; // verifico buco if ( Hole.GetArcNbr() == 0 || ! Hole.IsClosed()) return false ; // cerco il pezzo const auto Iter = m_IdPartPtr.find( nPartId) ; if ( Iter == m_IdPartPtr.end()) return false ; CNS_PartPtr pPart = Iter->second ; // aggiungo buco al pezzo vector vElem ; GetCNSPath( Hole, 0, 0, vElem) ; CNS_AddHoleToPart( pPart, int( vElem.size()), vElem.data()) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::AddAnotherOutlineToPart( int nPartId, const PolyArc& AnotherOutline) { if ( m_pOrder == nullptr) return false ; // verifico outline if ( AnotherOutline.GetArcNbr() == 0 || ! AnotherOutline.IsClosed()) return false ; // cerco il pezzo const auto Iter = m_IdPartPtr.find( nPartId) ; if ( Iter == m_IdPartPtr.end()) return false ; CNS_PartPtr pPart = Iter->second ; // aggiungo contorno aggiuntivo al pezzo vector vElem ; GetCNSPath( AnotherOutline, 0, 0, vElem) ; CNS_AddExternalBoundaryToPart( pPart, int( vElem.size()), vElem.data()) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::AddToolOutlineToPart( int nPartId, const PolyArc& ToolOutline) { if ( m_pOrder == nullptr) return false ; // verifico outline if ( ToolOutline.GetArcNbr() == 0 || ! ToolOutline.IsClosed()) return false ; // cerco il pezzo const auto Iter = m_IdPartPtr.find( nPartId) ; if ( Iter == m_IdPartPtr.end()) return false ; CNS_PartPtr pPart = Iter->second ; // aggiungo contorno di lavorazione vector vElem ; GetCNSPath( ToolOutline, 0, 0, vElem) ; CNS_AddToolPathToPart( pPart, int( vElem.size()), vElem.data()) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::SetRestrictedZoneToPart( int nPartId, int nRzConstrId) { if ( m_pOrder == nullptr) return false ; // cerco il pezzo const auto Iter = m_IdPartPtr.find( nPartId) ; if ( Iter == m_IdPartPtr.end()) return false ; CNS_PartPtr pPart = Iter->second ; // recupero il vincolo const auto IterC = m_IdRzCstrPtr.find( nRzConstrId) ; if ( IterC == m_IdRzCstrPtr.end()) return false ; CNS_RestrictedZoneConstraintPtr pRzCstr = IterC->second ; // imposto il vincolo CNS_SetZoneRestrictedPart( pRzCstr, pPart) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::SetStripYconstraintToPart( int nPartId, const Point3d& ptRef, double dStripStart, double dStripRepeat) { if ( m_pOrder == nullptr) return false ; // cerco il pezzo const auto Iter = m_IdPartPtr.find( nPartId) ; if ( Iter == m_IdPartPtr.end()) return false ; CNS_PartPtr pPart = Iter->second ; // imposto il vincolo CNS_Point CNSPT_Ref ; CNSPT_Ref.x = ptRef.x ; CNSPT_Ref.y = ptRef.y ; CNS_SetPartStripYconstraint( pPart, CNSPT_Ref, dStripStart, dStripRepeat) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::SetStripXconstraintToPart( int nPartId, const Point3d& ptRef, double dStripStart, double dStripRepeat) { if ( m_pOrder == nullptr) return false ; // cerco il pezzo const auto Iter = m_IdPartPtr.find( nPartId) ; if ( Iter == m_IdPartPtr.end()) return false ; CNS_PartPtr pPart = Iter->second ; // imposto il vincolo CNS_Point CNSPT_Ref ; CNSPT_Ref.x = ptRef.x ; CNSPT_Ref.y = ptRef.y ; CNS_SetPartStripXconstraint( pPart, CNSPT_Ref, dStripStart, dStripRepeat) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::SetInterpartGap( double dGap) { if ( m_pOrder == nullptr) return false ; m_dInterpartGap = max( 0., dGap) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::SetShearGap( double dShearGap) { if ( m_pOrder == nullptr) return false ; m_dShearGap = max( 0., dShearGap) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::SetReportFile( const string& sReportFile) { m_sReportFile = sReportFile ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::Compute( bool bMinimizeOnXvsY, int nMaxTime) { if ( m_pOrder == nullptr) return false ; // se necessario, imposto i gap ( di default sono a 0) if ( m_dInterpartGap > 10 * EPS_SMALL) { // gap tra i pezzi CNS_SetInterpartGap( m_pOrder, m_dInterpartGap) ; // gap sui difetti for ( auto Iter = m_IdSheetInfo.begin() ; Iter != m_IdSheetInfo.end() ; ++ Iter) CNS_SetDefectGap( Iter->second.pSheet, m_dInterpartGap) ; } // shear gap if ( m_dShearGap > 10 * EPS_SMALL) CNS_SetShearGap( m_pOrder, m_dShearGap) ; // per evitare pezzi posti inutilmente di sghembo su ultimo pannello CNS_SetObjective( m_pOrder, ( bMinimizeOnXvsY ? CNS_IntelligentMinimizeX : CNS_IntelligentMinimizeY)) ; // recupero i codici di sblocco del nesting string sKeyId, sKeySign ; SplitFirst( GetENsKey2(), "-", sKeyId, sKeySign) ; // verifico si riferiscano alla chiave di protezione in uso int nKeySN, nKeyId ; if ( ! GetLockSN( nKeySN) || ! FromString( sKeyId, nKeyId) || nKeySN != nKeyId) return false ; // passo i codici al nester CNS_UnLockLaunchingOrderOxy( m_pOrder, sKeyId.c_str(), sKeySign.c_str()) ; // se richiesto debug if ( ! IsEmptyOrSpaces( m_sReportFile)) CNS_GenerateLaunchingOrderProblem( m_pOrder, m_sReportFile.c_str()) ; // lancio l'esecuzione m_pComp = CNS_LaunchLocalComputation( m_pOrder, nMaxTime) ; return ( m_pComp != nullptr) ; } //---------------------------------------------------------------------------- bool AutoNester::CancelComputation( void) { if ( m_pOrder == nullptr || m_pComp == nullptr) return false ; CNS_ComputationStatus status = CNS_CancelComputation( m_pComp) ; return ( status == CNS_CancelledComputation || status == CNS_Ok) ; } //---------------------------------------------------------------------------- bool AutoNester::GetComputationStatus( int& nStatus) { if ( m_pOrder == nullptr || m_pComp == nullptr) return false ; CNS_ComputationStatus status = CNS_GetComputationStatus( m_pComp) ; if ( status == CNS_CancelledComputation) nStatus = 0 ; else if ( status == CNS_PendingComputation) nStatus = 1 ; else if ( status == CNS_IntermediateResult) nStatus = 2 ; else if ( status == CNS_Ok) nStatus = 3 ; else nStatus = - 1 ; return ( nStatus >= 0) ; } //---------------------------------------------------------------------------- bool AutoNester::GetResults( double& dTotFillRatio, ANIVECT& vANI) { // verifico validità calcolo if ( m_pComp == nullptr) return false ; // recupero la soluzione CNS_SolutionPtr pSol = CNS_GetSolution( m_pComp) ; if ( pSol == nullptr) return false ; // percentuale di riempimento complessivo dTotFillRatio = CNS_GetFillRatio( pSol) ; // recupero i dati di nesting vANI.clear() ; int nNestCount = CNS_GetNumberOfNestings( pSol) ; for ( int i = 0 ; i < nNestCount ; ++ i) { // riferimento allo i-esimo nesting CNS_NestingPtr pNest = CNS_GetNesting( pSol, i) ; // dati dello sheet CNS_SheetPtr pSheet = CNS_GetSheet( pNest) ; const char* szSheetId = CNS_GetSheetUserString( pSheet) ; int nSheetId = 999 ; if ( szSheetId != nullptr) FromString( szSheetId, nSheetId) ; int nMult = CNS_GetMultiplicity( pNest) ; int nPartCount = CNS_GetNumberOfNestedParts( pNest) ; double dFillRatio = CNS_GetNestingFillRatio( pNest) ; vANI.push_back( { nMult, nSheetId, nPartCount, dFillRatio, 0, 0}) ; // offset per origine sheet rettangolari const auto Iter = m_IdSheetInfo.find( nSheetId) ; double dOffsX = ( Iter != m_IdSheetInfo.end() ? Iter->second.dOffsX : 0) ; double dOffsY = ( Iter != m_IdSheetInfo.end() ? Iter->second.dOffsY : 0) ; // ciclo sui pezzi nestati for ( int j = 0; j < nPartCount ; ++ j) { // dati del pezzo CNS_PartPtr pPart ; double dX, dY, dAngRot ; int nFlip ; CNS_GetNestedPart( pNest, j, &pPart, &dX, &dY, &nFlip, &dAngRot) ; const char* szPartId = CNS_GetPartUserString( pPart) ; int nPartId = 999 ; if ( szPartId != nullptr) FromString( szPartId, nPartId) ; vANI.push_back( { 0, nPartId, nFlip, dOffsX + dX, dOffsY + dY, dAngRot}) ; } } return true ; } //---------------------------------------------------------------------------- bool AutoNester::CalcShearSequence( int nNesting, PNTVECTOR& vPtStart, PNTVECTOR& vPtEnd) { vPtStart.clear() ; vPtEnd.clear() ; // verifico se impostata modalità ghigliottina if ( ! m_bGuillotine) return false ; // verifico vincolo con intergap tra i pezzi if ( m_dShearGap < m_dInterpartGap - EPS_SMALL) return false ; // verifico validità calcolo if ( m_pComp == nullptr) return false ; // recupero la soluzione CNS_SolutionPtr pSol = CNS_GetSolution( m_pComp) ; if ( pSol == nullptr) return false ; // recupero il nesting richiesto int nNestCount = CNS_GetNumberOfNestings( pSol) ; if ( nNesting < 0 || nNesting > nNestCount - 1) return false ; CNS_NestingPtr pNest = CNS_GetNesting( pSol, nNesting) ; // recupero pannello CNS_SheetPtr pSheet = CNS_GetSheet( pNest) ; const char* szSheetId = CNS_GetSheetUserString( pSheet) ; int nSheetId = 999 ; if ( szSheetId != nullptr) FromString( szSheetId, nSheetId) ; const auto Iter = m_IdSheetInfo.find( nSheetId) ; // verifico vincolo con kerf del pannello double dSheetKerf = ( Iter != m_IdSheetInfo.end() ? Iter->second.dKerf : 0) ; if ( dSheetKerf > EPS_SMALL && m_dShearGap > dSheetKerf + EPS_SMALL) return false ; // offset per origine pannello double dOffsX = ( Iter != m_IdSheetInfo.end() ? Iter->second.dOffsX : 0) ; double dOffsY = ( Iter != m_IdSheetInfo.end() ? Iter->second.dOffsY : 0) ; // calcolo shear sequence CNS_ShearSequencePtr pShearSeq = CNS_ComputeShearSequence( pNest) ; int nCnt = CNS_GetNumberOfElementsInShearSequence( pShearSeq) ; vPtStart.reserve( nCnt) ; vPtEnd.reserve( nCnt) ; for ( int j = 0 ; j < nCnt ; j ++) { double dStartX, dEndX, dStartY, dEndY ; CNS_GetElementInShearSequence( pShearSeq, j, &dStartX, &dStartY, &dEndX, &dEndY) ; vPtStart.emplace_back( dStartX + dOffsX, dStartY + dOffsY, 0) ; vPtEnd.emplace_back( dEndX + dOffsX, dEndY + dOffsY, 0) ; } CNS_DeleteShearSequence( pShearSeq) ; return true ; } //---------------------------------------------------------------------------- bool AutoNester::PrintResults( const string& sHtmlFile) { // verifico validità calcolo if ( m_pComp == nullptr) return false ; // recupero la soluzione CNS_SolutionPtr pSol = CNS_GetSolution( m_pComp) ; if ( pSol == nullptr) return false ; // se richiesto risultato in html if ( ! IsEmptyOrSpaces( sHtmlFile)) CNS_GenerateHtmlSolutionReport( pSol, sHtmlFile.c_str()) ; return true ; }