2d7630de76
- aggiunta funzione SetGuillotineMode (modalità ghigliottina funziona solo per Sheet rettangolari e Pezzi senza ToolOutline).
418 lines
14 KiB
C++
418 lines
14 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2019-2019
|
|
//----------------------------------------------------------------------------
|
|
// File : AutoNester.cpp Data : 05.12.19 Versione : 2.1l2
|
|
// Contenuto : Implementazione della classe AutoNester.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 28.11.19 DS Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- 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_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 false ;
|
|
// creo il NestMgr
|
|
return static_cast<IAutoNester*> ( new(nothrow) AutoNester) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static void
|
|
GetCNSPath( const PolyArc& Outline, double dOffsX, double dOffsY, vector<CNS_Element>& 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)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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_IdPartPtr.clear() ;
|
|
m_dInterpartGap = 0 ;
|
|
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) ;
|
|
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<CNS_Element> 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)) ;
|
|
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<CNS_Element> vElem ;
|
|
GetCNSPath( Outline, -dOffsX, -dOffsY, vElem) ;
|
|
CNS_AddDefectToSheet( 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<CNS_Element> vElem ;
|
|
GetCNSPath( Outline, 0, 0, vElem) ;
|
|
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<CNS_Element> 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<CNS_Element> 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<CNS_Element> vElem ;
|
|
GetCNSPath( ToolOutline, 0, 0, vElem) ;
|
|
CNS_AddToolPathToPart( pPart, int( vElem.size()), vElem.data()) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
AutoNester::SetInterpartGap( double dGap)
|
|
{
|
|
if ( m_pOrder == nullptr)
|
|
return false ;
|
|
m_dInterpartGap = max( 0., dGap) ;
|
|
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) ;
|
|
}
|
|
// 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::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 ;
|
|
}
|
|
|