diff --git a/MachOptimization.cpp b/MachOptimization.cpp index 77c16e4..40fb04c 100644 --- a/MachOptimization.cpp +++ b/MachOptimization.cpp @@ -60,6 +60,7 @@ MachOptimization::MachOptimization( void) m_dFeedA = 1. ; m_bAllMandatory = false ; m_bOptInGroup = false ; + m_vOpenBounds.clear() ; } //---------------------------------------------------------------------------- @@ -242,6 +243,44 @@ MachOptimization::SetOptimizationForGroups( bool bOptForGroups) return true ; } +//----------------------------------------------------------------------------- +bool +MachOptimization::SetOpenBound( bool bStartVsEnd, int nFlag, double dX, double dY, double dZ) +{ + // Se parametro Start o End già impostato, lo sovrascrivo + for ( int i = 0 ; i < int( m_vOpenBounds.size()) ; ++ i) { + if ( m_vOpenBounds[i].bStartVsEnd == bStartVsEnd) { + m_vOpenBounds[i].nFlag = nFlag ; + m_vOpenBounds[i].dX = dX ; + m_vOpenBounds[i].dY = dY ; + m_vOpenBounds[i].dZ = dZ ; + return true ; + } + } + // altrimenti lo inserisco + m_vOpenBounds.emplace_back( -1, bStartVsEnd, nFlag, dX, dY, dZ) ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +MachOptimization::SetOpenBoundForGroups( int nGroup, bool bStartVsEnd, int nFlag, double dX, double dY, double dZ) +{ + // Se parametro Start o End del Gruppo scelto già impostato, lo sovrascrivo + for ( int i = 0 ; i < int( m_vOpenBounds.size()) ; ++ i) { + if ( m_vOpenBounds[i].nGroup == nGroup && m_vOpenBounds[i].bStartVsEnd == bStartVsEnd) { + m_vOpenBounds[i].nFlag = nFlag ; + m_vOpenBounds[i].dX = dX ; + m_vOpenBounds[i].dY = dY ; + m_vOpenBounds[i].dZ = dZ ; + return true ; + } + } + // altrimenti lo inserisco + m_vOpenBounds.emplace_back( nGroup, bStartVsEnd, nFlag, dX, dY, dZ) ; + return true ; +} + //---------------------------------------------------------------------------- /* Funzione che assegna le dipendenze suggerite in base ai Gruppi */ bool @@ -290,7 +329,8 @@ MachOptimization::SetGroupDependences( void) //---------------------------------------------------------------------------- /* Funzione per il controllo delle dipendenze inserite : se già presenti o se in conflitto */ bool -MachOptimization::CheckDependences( int nIdPrev, int nIdNext, const INTINTVECTOR& vDep, bool bBlockMessages, bool& bExist, bool& bConflict) +MachOptimization::CheckDependences( int nIdPrev, int nIdNext, const INTINTVECTOR& vDep, + bool bBlockMessages, bool& bExist, bool& bConflict) const { bExist = false ; bConflict = false ; @@ -334,7 +374,7 @@ MachOptimization::CheckDependences( int nIdPrev, int nIdNext, const INTINTVECTOR /* Funzione per il calcolo della distanza euclidea tra assi Lineari o Angolari di due lavorazioni Considerando lo stesso utensile */ double -MachOptimization::CalcTimeLavLav( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin) +MachOptimization::CalcTimeLavLav( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin) const { double dDist = 0. ; double dFeed = EPS_SMALL ; @@ -361,7 +401,7 @@ MachOptimization::CalcTimeLavLav( const MachOptm& Lav1, const MachOptm& Lav2, bo bool MachOptimization::CalcTimeLavLavTC( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin, double& dTimeLav1TC1, double& dTimeTC1TC2, double& dTimeTC2Lav2, - double& dTUnload1, double& dTLoad2) + double& dTUnload1, double& dTLoad2) const { /* dTimeLav1TC1 : Tempo tra EndPoint di Lav1 e Posizione di TC per Utensile di Lav1 @@ -466,7 +506,7 @@ MachOptimization::CalcTimeLavLavTC( const MachOptm& Lav1, const MachOptm& Lav2, /* Funzione per calcolare il tempo nel passare dalla lavorazione nInd1-esima alla lavorazione nInd2-esima */ bool -MachOptimization::CalcMachTime( int nInd1, int nInd2, int& nTime) +MachOptimization::CalcMachTime( int nInd1, int nInd2, int& nTime) const { // Se Indice fuori dal range, errore nTime = 0 ; @@ -475,8 +515,8 @@ MachOptimization::CalcMachTime( int nInd1, int nInd2, int& nTime) return false ; // Recupero riferimento delle due lavorazioni - MachOptm& Lav1 = m_vMachOptm[nInd1] ; - MachOptm& Lav2 = m_vMachOptm[nInd2] ; + const MachOptm& Lav1 = m_vMachOptm[nInd1] ; + const MachOptm& Lav2 = m_vMachOptm[nInd2] ; return ( CalcMachTime( Lav1, Lav2, nTime)) ; } @@ -484,7 +524,7 @@ MachOptimization::CalcMachTime( int nInd1, int nInd2, int& nTime) //---------------------------------------------------------------------------- /* Funzione per calcolare il tempo nel passare da una lavorazione all'altra */ bool -MachOptimization::CalcMachTime( const MachOptm& Lav1, const MachOptm& Lav2, int& nTime) +MachOptimization::CalcMachTime( const MachOptm& Lav1, const MachOptm& Lav2, int& nTime) const { // Se l'utensile è lo stesso if ( Lav1.nIdTool == Lav2.nIdTool) @@ -508,7 +548,7 @@ MachOptimization::CalcMachTime( const MachOptm& Lav1, const MachOptm& Lav2, int& //---------------------------------------------------------------------------- /* Funzione per il calcolo della Matrice dei tempi (NxN), dove N = #Lavorazioni */ bool -MachOptimization::ComputeTimeMatrix( INTMATRIX& mTimes) +MachOptimization::ComputeTimeMatrix( INTMATRIX& mTimes) const { // Creo una copia dei vettori delle dipendenze ( le cancello di volta in volta che le assegno) const int N = int( m_vMachOptm.size()) ; @@ -578,7 +618,7 @@ MachOptimization::ComputeTimeMatrix( INTMATRIX& mTimes) //---------------------------------------------------------------------------- /* Funzione che restituisce la posizione della lavorazione di Id pari a nId nel vettore delle lavorazioni */ int -MachOptimization::GetIndById( int nId) +MachOptimization::GetIndById( int nId) const { // Scorro le lavorazioni for ( int i = 0 ; i < int( m_vMachOptm.size()) ; ++ i) { @@ -654,135 +694,114 @@ MachOptimization::SplitMachOptsInGroups( MACHOPTMMATRIX& matMachOpt, vector matDep, matSuggDep ; - if ( ! SplitMachOptsInGroups( matMachOpt, matDep, matSuggDep)) - return false ; - // Per ognuna di esse eseguo l'algoritmo di ShortestPath - MachOptm LastMach ; - for ( int i = 0 ; i < int( matMachOpt.size()) ; ++ i) { - m_vMachOptm.clear() ; - m_vDependences.clear() ; - m_vSuggDependences.clear() ; - if ( matMachOpt.empty()) - continue ; - m_vMachOptm = matMachOpt[i] ; - m_vDependences = matDep[i] ; - m_vSuggDependences = matSuggDep[i] ; - // Calcolo la matrice dei tempi - INTMATRIX mTimes ; - if ( ! ComputeTimeMatrix( mTimes)) { - LOG_ERROR( GetENkLogger(), "Error in Computing Time Matrix") ; - return false ; - } - // Istanzio il Problema ShortesPath con vincoli - PtrOwner mySP( CreateShortestPath()) ; - if ( IsNull( mySP)) - return false ; - // Per ogni lavorazione inserisco gli estremi - for ( const MachOptm& Lav : m_vMachOptm) { - if ( ! mySP->AddPoint( Lav.dX_Start, Lav.dY_Start, Lav.dZ_End, 0., 0., - Lav.dX_End, Lav.dY_End, Lav.dZ_End, 0., 0.)) { - LOG_ERROR( GetENkLogger(), "Error adding Machining Bounds") ; - return false ; - } - } - // Assegno la matrice delle distanze calcolata in precedenza - if ( ! mySP->SetDistMatrix( mTimes)) { - LOG_ERROR( GetENkLogger(), "Error setting distances matrix") ; - return false ; - } - // Se ho una lavorazione impostata in precedenza - if ( LastMach.nId != -1) { - // Cerco le lavorazioni che non presentano precedenze - MACHOPTMVECTOR vLav ; vLav.reserve( m_vMachOptm.size()) ; - if ( m_vDependences.empty()) - vLav = m_vMachOptm ; - else { - INTSET setSecondDep ; - for ( const INTINT& Dep : m_vDependences) - setSecondDep.insert( Dep.second) ; - for ( int i = 0 ; i < int( m_vMachOptm.size()) ; ++ i) { - if ( setSecondDep.find( m_vMachOptm[i].nId) == setSecondDep.end()) - vLav.emplace_back( m_vMachOptm[i]) ; - } - } - // Tra quelle valide cerco la migliore da cui partire - int nBestLavId = 0 ; - int nBestTime = MAXTIME ; - for ( int i = 0 ; i < int( vLav.size()) ; ++ i) { - int nCurrTime = MAXTIME ; - CalcMachTime( LastMach, vLav[i], nCurrTime) ; - if ( nCurrTime < nBestTime) { - nBestTime = nCurrTime ; - nBestLavId = vLav[i].nId ; - } - } - // Imposto la prima lavorazione - if ( ! SetFirstMachining( nBestLavId)) - return false ; - } - // Per ogni dipendeza obbligatoria assegno un vincolo - for ( const INTINT& nnDep : m_vDependences) { - if ( ! mySP->SetConstraintOrder( GetIndById( nnDep.first), GetIndById( nnDep.second))) { - LOG_ERROR( GetENkLogger(), "Error in adding Constraints") ; - return false ; - } - } - // Aggiungo le dipendenza suggerite - for ( const INTINT& nnSuggDep : m_vSuggDependences) { - if ( ! mySP->SetSuggestedOrder( GetIndById( nnSuggDep.first), GetIndById( nnSuggDep.second))) { - LOG_ERROR( GetENkLogger(), "Error in adding Suggested Dependence") ; - return false ; - } - } - // Calcolo la soluzione - #if _TIME_DEBUG - PerformanceCounter PC ; PC.Start() ; - #endif - if ( ! mySP->Calculate( IShortestPath::SP_OPEN)) { - LOG_ERROR( GetENkLogger(), "Error in computing solution") ; - return false ; - } - // Recupero l'ordine - INTVECTOR U ; - if ( ! mySP->GetOrder( U)) { - LOG_ERROR( GetENkLogger(), "Error in ordering") ; - return false ; - } - // Restituisco gli Id ordinati - for ( int nInd = 0 ; nInd < int( U.size()) ; ++ nInd) - vIds.emplace_back( m_vMachOptm[U[nInd]].nId) ; - // Memorizzo l'ultima lavorazione per il gruppo successivo - LastMach = ( U.empty() ? MachOptm() : m_vMachOptm[U.back()]) ; - } + // Calcolo la matrice dei tempi + INTMATRIX mTimes ; + if ( ! ComputeTimeMatrix( mTimes)) { + LOG_ERROR( GetENkLogger(), "Error in Computing Time Matrix") ; + return false ; } - - //--- Se invece ottimizzazione globale - else { - // Inserisco le lavorazioni per ordine di inserimento - for ( const MachOptm& myLav : m_vMachOptm) - vIds.push_back( myLav.nId) ; - // Inizializzo le dipendenze suggerite dai gruppi di lavorazione - if ( ! SetGroupDependences()) { - LOG_ERROR( GetENkLogger(), "Error in defining Group depedences") ; + // Istanzio il Problema ShortesPath con vincoli + PtrOwner mySP( CreateShortestPath()) ; + if ( IsNull( mySP)) + return false ; + // Per ogni lavorazione inserisco gli estremi + for ( const MachOptm& Lav : m_vMachOptm) { + if ( ! mySP->AddPoint( Lav.dX_Start, Lav.dY_Start, Lav.dZ_End, 0., 0., + Lav.dX_End, Lav.dY_End, Lav.dZ_End, 0., 0.)) { + LOG_ERROR( GetENkLogger(), "Error adding Machining Bounds") ; return false ; } + } + // Assegno la matrice delle distanze calcolata in precedenza + if ( ! mySP->SetDistMatrix( mTimes)) { + LOG_ERROR( GetENkLogger(), "Error setting distances matrix") ; + return false ; + } + // Per ogni dipendeza obbligatoria assegno un vincolo + for ( const INTINT& nnDep : m_vDependences) { + if ( ! mySP->SetConstraintOrder( GetIndById( nnDep.first), GetIndById( nnDep.second))) { + LOG_ERROR( GetENkLogger(), "Error in adding Constraints") ; + return false ; + } + } + // Aggiungo le dipendenza suggerite + for ( const INTINT& nnSuggDep : m_vSuggDependences) { + if ( ! mySP->SetSuggestedOrder( GetIndById( nnSuggDep.first), GetIndById( nnSuggDep.second))) { + LOG_ERROR( GetENkLogger(), "Error in adding Suggested Dependence") ; + return false ; + } + } + // Se presenti dei incoli di OpenBound, li assegno + if ( ! m_vOpenBounds.empty()) { + for ( const OpenBoundOptm& OB : m_vOpenBounds) { + if ( OB.nGroup == -1) { + if ( ! mySP->SetOpenBound( OB.bStartVsEnd, OB.nFlag, OB.dX, OB.dY, OB.dZ, 0., 0.)) { + LOG_ERROR( GetENkLogger(), "Error in adding OpenBound") ; + return false ; + } + } + } + } + // Calcolo la soluzione + #if _TIME_DEBUG + PerformanceCounter PC ; PC.Start() ; + #endif + if ( ! mySP->Calculate( IShortestPath::SP_OPEN)) { + LOG_ERROR( GetENkLogger(), "Error in computing solution") ; + return false ; + } + #if _TIME_DEBUG + string sTime = string( to_string( m_vDependences.size()) + string( "Time : ") + ToString( PC.Stop())) ; + LOG_INFO( GetENkLogger(), sTime.c_str()) ; + #endif + // Recupero l'ordine + INTVECTOR U ; + if ( ! mySP->GetOrder( U)) { + LOG_ERROR( GetENkLogger(), "Error in ordering") ; + return false ; + } + // Restituisco gli Id ordinati + vIds.resize( m_vMachOptm.size()) ; + for ( int nInd = 0 ; nInd < int( U.size()) ; ++ nInd) + vIds[nInd] = m_vMachOptm[U[nInd]].nId ; + + return true ; +} + +//---------------------------------------------------------------------------- +bool +MachOptimization::GetGroupResult( INTVECTOR& vIds) +{ + // Se Flag ottimizzazione di gruppo non attivo, errore + if ( ! m_bOptInGroup) + return false ; + + // Recupero tutti i gruppi e le dipendenze separatamente ed ordinate per priorità di gruppo + MACHOPTMMATRIX matMachOpt ; + vector matDep, matSuggDep ; + if ( ! SplitMachOptsInGroups( matMachOpt, matDep, matSuggDep)) + return false ; + // Per ognuna di esse eseguo l'algoritmo di ShortestPath + MachOptm LastMach ; + for ( int i = 0 ; i < int( matMachOpt.size()) ; ++ i) { + m_vMachOptm.clear() ; + m_vDependences.clear() ; + m_vSuggDependences.clear() ; + if ( matMachOpt.empty()) + continue ; + m_vMachOptm = matMachOpt[i] ; + m_vDependences = matDep[i] ; + m_vSuggDependences = matSuggDep[i] ; // Calcolo la matrice dei tempi INTMATRIX mTimes ; if ( ! ComputeTimeMatrix( mTimes)) { @@ -806,6 +825,47 @@ MachOptimization::GetResult( INTVECTOR& vIds) LOG_ERROR( GetENkLogger(), "Error setting distances matrix") ; return false ; } + // Se presente un vincolo OpenBound lo assegno + bool bForStart = false ; + for ( const OpenBoundOptm& OB : m_vOpenBounds) { + if ( OB.nGroup == m_vMachOptm[0].nGroup) { + bForStart = ( bForStart || OB.bStartVsEnd) ; + if ( ! mySP->SetOpenBound( OB.bStartVsEnd, OB.nFlag, OB.dX, OB.dY, OB.dZ, 0., 0.)) { + LOG_ERROR( GetENkLogger(), "Error in adding OpenBound") ; + return false ; + } + } + } + // Se ho una lavorazione impostata in precedenza e nessun vincolo OpenBound + if ( ! bForStart && LastMach.nId != -1) { + // Cerco le lavorazioni che non presentano precedenze + MACHOPTMVECTOR vLav ; vLav.reserve( m_vMachOptm.size()) ; + if ( m_vDependences.empty()) + vLav = m_vMachOptm ; + else { + INTSET setSecondDep ; + for ( const INTINT& Dep : m_vDependences) + setSecondDep.insert( Dep.second) ; + for ( int i = 0 ; i < int( m_vMachOptm.size()) ; ++ i) { + if ( setSecondDep.find( m_vMachOptm[i].nId) == setSecondDep.end()) + vLav.emplace_back( m_vMachOptm[i]) ; + } + } + // Tra quelle valide cerco la migliore da cui partire + int nBestLavId = 0 ; + int nBestTime = MAXTIME ; + for ( int i = 0 ; i < int( vLav.size()) ; ++ i) { + int nCurrTime = MAXTIME ; + CalcMachTime( LastMach, vLav[i], nCurrTime) ; + if ( nCurrTime < nBestTime) { + nBestTime = nCurrTime ; + nBestLavId = vLav[i].nId ; + } + } + // Imposto la prima lavorazione + if ( ! SetFirstMachining( nBestLavId)) + return false ; + } // Per ogni dipendeza obbligatoria assegno un vincolo for ( const INTINT& nnDep : m_vDependences) { if ( ! mySP->SetConstraintOrder( GetIndById( nnDep.first), GetIndById( nnDep.second))) { @@ -828,10 +888,6 @@ MachOptimization::GetResult( INTVECTOR& vIds) LOG_ERROR( GetENkLogger(), "Error in computing solution") ; return false ; } - #if _TIME_DEBUG - string sTime = string( to_string( m_vDependences.size()) + string( "Time : ") + ToString( PC.Stop())) ; - LOG_INFO( GetENkLogger(), sTime.c_str()) ; - #endif // Recupero l'ordine INTVECTOR U ; if ( ! mySP->GetOrder( U)) { @@ -839,10 +895,32 @@ MachOptimization::GetResult( INTVECTOR& vIds) return false ; } // Restituisco gli Id ordinati - vIds.resize( m_vMachOptm.size()) ; for ( int nInd = 0 ; nInd < int( U.size()) ; ++ nInd) - vIds[nInd] = m_vMachOptm[U[nInd]].nId ; + vIds.emplace_back( m_vMachOptm[U[nInd]].nId) ; + // Memorizzo l'ultima lavorazione per il gruppo successivo + LastMach = ( U.empty() ? MachOptm() : m_vMachOptm[U.back()]) ; } return true ; } + +//---------------------------------------------------------------------------- +bool +MachOptimization::GetResult( INTVECTOR& vIds) +{ + // Se non ho lavorazioni, allora non faccio nulla + vIds.clear() ; + if ( m_vMachOptm.empty()) + return true ; + // Se ho una sola lavorazione allora ritorno se stessa + if ( int( m_vMachOptm.size()) == 1) { + vIds.push_back( m_vMachOptm[0].nId) ; + return true ; + } + + // --- Se richiesta ottimizzazione solo interna per i gruppi + if ( m_bOptInGroup) + return ( GetGroupResult( vIds)) ; + // --- Se invece ottimizzazione globale + return ( GetGlobalResult( vIds)) ; +} diff --git a/MachOptimization.h b/MachOptimization.h index ff8dc8b..3532fe2 100644 --- a/MachOptimization.h +++ b/MachOptimization.h @@ -39,6 +39,8 @@ class MachOptimization : public IMachOptimization bool SetFeeds( double dFeedL, double dFeedA) override ; bool SetAllGroupsAsMandatory( bool bAllMandatory) override ; bool SetOptimizationForGroups( bool bOptForGroups) override ; + bool SetOpenBound( bool bStartVsEnd, int nFlag, double dX, double dY, double dZ) override ; + bool SetOpenBoundForGroups( int nGroup, bool bStartVsEnd, int nFlag, double dX, double dY, double dZ) override ; // Risultati bool GetResult( INTVECTOR& vIds) override ; @@ -48,33 +50,38 @@ class MachOptimization : public IMachOptimization private : // Calcolo dei tempi tra le lavorazioni - double CalcTimeLavLav( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin) ; + double CalcTimeLavLav( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin) const ; bool CalcTimeLavLavTC( const MachOptm& Lav1, const MachOptm& Lav2, bool bLin, double& dTimeLav1TC1, double& dTimeTC1TC2, double& dTimeTC2Lav2, - double& dTUnload1, double& dTLoad2) ; - bool CalcMachTime( int nInd1, int nInd2, int& nTime) ; - bool CalcMachTime( const MachOptm& Lav1, const MachOptm& Lav2, int& nTime) ; + double& dTUnload1, double& dTLoad2) const ; + bool CalcMachTime( int nInd1, int nInd2, int& nTime) const ; + bool CalcMachTime( const MachOptm& Lav1, const MachOptm& Lav2, int& nTime) const ; // Controllo Conflitti tra dipendenze e Gestione Dipendenze bool CheckDependences( int nIdPrev, int nIdNext, const INTINTVECTOR& vDep, - bool bBlockMessages, bool& bExist, bool& bConflict) ; + bool bBlockMessages, bool& bExist, bool& bConflict) const ; bool SetGroupDependences( void) ; // Calcolo della Matrice dei tempi, Del fattore perso dipendenze suggerite e della soluzione ottimale - bool ComputeTimeMatrix( INTMATRIX& mTimes) ; + bool ComputeTimeMatrix( INTMATRIX& mTimes) const ; + + // Calcolo dei risultati a seconda della modalità + bool GetGlobalResult( INTVECTOR& vIds) ; + bool GetGroupResult( INTVECTOR& vIds) ; // Utility - int GetIndById( int nId) ; + int GetIndById( int nId) const ; bool SplitMachOptsInGroups( MACHOPTMMATRIX& matMachOpt, vector& matDep, vector& matSuggDep) ; private : - MACHOPTMVECTOR m_vMachOptm ; // Vettore delle lavorazioni - TOOLOPTMVECTOR m_vToolOptm ; // Vettore degli utensili - INTINTVECTOR m_vDependences ; // Vettore di dipendenze ( IdPrev, IdSucc) -> deve essere rispettato - INTINTVECTOR m_vSuggDependences ; // Vettore delle dipendenze suggerite ( IdPrev, IdSucc) -> si cerca di rispettarle - double m_dFeedL ; // Feed Lineare - double m_dFeedA ; // Feed Angolare - bool m_bAllMandatory ; // Flag per impostare ogni dipendenza da Gruppo come vincolo - bool m_bOptInGroup ; // Flag per ottimizzazione solo interna ai gruppi + MACHOPTMVECTOR m_vMachOptm ; // Vettore delle lavorazioni + TOOLOPTMVECTOR m_vToolOptm ; // Vettore degli utensili + INTINTVECTOR m_vDependences ; // Vettore di dipendenze ( IdPrev, IdSucc) -> deve essere rispettato + INTINTVECTOR m_vSuggDependences ; // Vettore delle dipendenze suggerite ( IdPrev, IdSucc) -> si cerca di rispettarle + double m_dFeedL ; // Feed Lineare + double m_dFeedA ; // Feed Angolare + bool m_bAllMandatory ; // Flag per impostare ogni dipendenza da Gruppo come vincolo + bool m_bOptInGroup ; // Flag per ottimizzazione solo interna ai gruppi + MACHOPTOPENBOUNDVECTOR m_vOpenBounds ; // Vettore dei punti OpenBound } ;