//---------------------------------------------------------------------------- // EgalTech 2015-2023 //---------------------------------------------------------------------------- // File : Drilling.cpp Data : 20.12.23 Versione : 2.5l47 // Contenuto : Implementazione gestione forature. // // Note : Questa lavorazione è sempre espressa nel riferimento globale. // // Modifiche : 21.05.15 DS Creazione modulo. // 20.12.23 RE Forature multiple con aggregati. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "MachMgr.h" #include "DllMain.h" #include "Drilling.h" #include "OperationConst.h" #include "OperUserNotesConst.h" #include "/EgtDev/Include/EXeCmdLogOff.h" #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkGeoVector3d.h" #include "/EgtDev/Include/EGkCurveLine.h" #include "/EgtDev/Include/EGkCurveArc.h" #include "/EgtDev/Include/EGkCurveComposite.h" #include "/EgtDev/Include/EGkChainCurves.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EGkUserObjFactory.h" #include "/EgtDev/Include/EGnStringKeyVal.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include "/EgtDev/Include/EGkGeoFrame3d.h" #include "/EgtDev/Include/EgtIniFile.h" using namespace std ; //------------------------------ Errors -------------------------------------- // 2101 = "Error in Drilling : UpdateToolData failed" // 2102 = "Error in Drilling : Offset not computable" // 2103 = "Error in Drilling : Overlap not computable" // 2104 = "Error in Drilling : Depth not computable" // 2105 = "Error in Drilling : Drill with zero depth" // 2106 = "Error in Drilling : Entity xx skipped because missing aggregate from bottom" // 2107 = "Error in Drilling : Entity xx skipped because too far from part sides" // 2108 = "Error in Drilling : Chaining failed" // 2109 = "Error in Drilling : axes values not calculable" // 2110 = "Error in Drilling : outstroke xx" // 2111 = "Error in Drilling : link movements not calculable" // 2112 = "Error in Drilling : link outstroke xx" // 2113 = "Error in Drilling : post apply not calculable" // 2114 = "Error in Drilling : blind hole not reversible" // 2115 = "Error in Drilling : Mirror for Double calculation failed" // 2116 = "Error in Drilling : multi drilling head without valid tools" // 2117 = "Error in Drilling : incorrect multi drilling head" // 2118 = "Error in Drilling : Tool loading failed" // 2119 = "Error in Drilling : special apply not calculable" // 2151 = "Warning in Drilling : Skipped entity (xx)" // 2152 = "Warning in Drilling : No machinable path" // 2153 = "Warning in Drilling : Tool name changed (xx)" // 2154 = "Warning in Drilling : Tool data changed (xx)" // 2155 = "Warning in Drilling : Drill bit too short for Hole " // 2156 = "Warning in Drilling : Skipped by Standard Drilling " // 2157 = "Warning in Drilling : Skipped by Peck Drilling " // 2158 = "Warning in Drilling : Skipped & PartialDrilled (xx) | (xx, L:yy) //---------------------------------------------------------------------------- // debug #define ENABLE_DEBUG_MULTIHEAD_HOLES 0 //---------------------------------------------------------------------------- // definizione delle costanti const int IND_TOOL_INVALID = -1 ; const int IND_HOLE_INVALID = -1 ; //---------------------------------------------------------------------------- // struttura semplificata del foro struct Hole { int nOriId ; // identificativo della geometria di origine Point3d ptIni ; // punto iniziale Vector3d vtDir ; // direzione di riferimento (dal fondo verso l'inizio) double dDiam ; // diametro double dThick ; // lunghezza della geometria originale double dLen ; // lunghezza dell'asse bool bBlind ; // flag per cieco/passante // eventuale tipo ( standard, ribassato, svasato) ? Hole( void) : nOriId( GDB_ID_NULL), ptIni(), vtDir(), dDiam( 0), dThick( 0), dLen( 0), bBlind( true) {} } ; // struttura avanzata per fori nel caso di MultiHeadDrills struct HoleInfo { Hole hole ; // foro bool bDrill = false ; // flag per indicare se il foro alla fine verrà lavorato bool bTempDrill = false ; // [per test maschera ( se durante un test viene lavorato o no)] int nTempTool = IND_TOOL_INVALID ; // [per test maschera ( indice utensile che lo lavora durante un test)] bool bForToolM = false ; // flag per indicare se il tool main lavorerà questo foro int nIndHoleToolM = IND_HOLE_INVALID ; // se il foro verrà lavorato, contiene l'indice del foro del tool principale // --- vettori ordinati --- INTVECTOR vToolHole ; // []-> indice del foro di inserimento del tool principale INTVECTOR vIndTools ; // []-> indice del tool che lavorerà il foro corrente VCT3DVECTOR vVtAux ; // []-> vettore ausiliario per tale condifgurazione INTVECTOR vIndMainTool ; // []-> solo nel caso di utensili selezionabili, contiene l'indice del tool considerato come principale int nIndTool = IND_TOOL_INVALID ; // indice del tool che alla fine lavorerà il foro Vector3d vtAux ; // vettore ausiliario del tool principale per lavorare il foro corrente Point3d ptBtn ; // punto interno alla base del foro int nIndInSelVector = 0 ; // indice nel vettore m_vId ( id fori selezionati) } ; // struttura avanzata per il tool, per MultiHeadDrills struct ToolInfo { const ToolData* pTool ; // puntatore per utensile ( nullo se utensile non presente) Point3d ptToolTip ; // punto finale del tool ( per ptBtn del foro) ToolInfo( void) : pTool( nullptr) {} ToolInfo( const ToolData* pT) : pTool( pT) {} } ; // struttura per foratura con MultiHeadDrills struct MHDrill { int nHoleInd ; // indice del foro Vector3d vtAux ; // vettore ausiliario per tale foratura INTVECTOR vActiveExit ; // nel caso di più utensili selezionabili, contiene gli indici delle uscite attive ToolInfo currTool ; // utensile da inserire nel foro di indice nHoleInd ( per utensili selezionabili) INTINTVECTOR vMatMask ; // vettore per indicare corrispondenze utensili e fori MHDrill( int nHInd, Vector3d vtA) : nHoleInd( nHInd), vtAux( vtA) {} ; MHDrill( int nHInd, Vector3d vtA, INTVECTOR vAE, ToolInfo cT) : nHoleInd( nHInd), vtAux( vtA), vActiveExit( vAE), currTool( cT) {} ; MHDrill( int nHInd, Vector3d vtA, INTVECTOR vAE, ToolInfo cT, INTINTVECTOR mSK) : nHoleInd( nHInd), vtAux( vtA), vActiveExit( vAE), currTool( cT), vMatMask( mSK) {} ; } ; //---------------------------------------------------------------------------- USEROBJ_REGISTER( GetOperationClass( OPER_DRILLING), Drilling) ; //---------------------------------------------------------------------------- const string& Drilling::GetClassName( void) const { return USEROBJ_GETNAME( Drilling) ; } //---------------------------------------------------------------------------- Drilling* Drilling::Clone( void) const { // alloco oggetto Drilling* pDri = new(nothrow) Drilling ; // eseguo copia dei dati if ( pDri != nullptr) { try { pDri->m_vId = m_vId ; pDri->m_pMchMgr = m_pMchMgr ; pDri->m_nPhase = m_nPhase ; pDri->m_Params = m_Params ; pDri->m_TParams = m_TParams ; pDri->m_nStatus = m_nStatus ; pDri->m_nDrillings = m_nDrillings ; pDri->m_bTiltingTab = m_bTiltingTab ; pDri->m_vtTiltingAx = m_vtTiltingAx ; pDri->m_bAboveHead = m_bAboveHead ; pDri->m_bAggrBottom = m_bAggrBottom ; pDri->m_vUndrilledId = m_vUndrilledId ; } catch( ...) { delete pDri ; return nullptr ; } } // ritorno l'oggetto return pDri ; } //---------------------------------------------------------------------------- bool Drilling::Dump( string& sOut, bool bMM, const char* szNewLine) const { sOut += GetClassName() + "[mm]" + szNewLine ; sOut += KEY_PHASE + EQUAL + ToString( m_nPhase) + szNewLine ; sOut += KEY_IDS + EQUAL + ToString( m_vId) + szNewLine ; for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) sOut += m_Params.ToString( i) + szNewLine ; for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i) sOut += m_TParams.ToString( i) + szNewLine ; sOut += KEY_NUM + EQUAL + ToString( m_nDrillings) + szNewLine ; sOut += KEY_STAT + EQUAL + ToString( m_nStatus) + szNewLine ; sOut += KEY_SKIPS + EQUAL + ToString( m_vUndrilledId) + szNewLine ; return true ; } //---------------------------------------------------------------------------- bool Drilling::Save( int nBaseId, STRVECTOR& vString) const { try { int nSize = 1 + m_Params.GetSize() + m_TParams.GetSize() + 3 + ( m_vUndrilledId.empty() ? 0 : 1) ; vString.insert( vString.begin(), nSize, "") ; int k = - 1 ; if ( ! SetVal( KEY_IDS, m_vId, vString[++k])) return false ; for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) vString[++k] = m_Params.ToString( i) ; for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i) vString[++k] = m_TParams.ToString( i) ; if ( ! SetVal( KEY_PHASE, m_nPhase, vString[++k])) return false ; if ( ! SetVal( KEY_NUM, m_nDrillings, vString[++k])) return false ; if ( ! SetVal( KEY_STAT, m_nStatus, vString[++k])) return false ; if ( ! m_vUndrilledId.empty() && ! SetVal( KEY_SKIPS, m_vUndrilledId, vString[++k])) return false ; } catch( ...) { return false ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::Load( const STRVECTOR& vString, int nBaseGdbId) { int nSize = int( vString.size()) ; // lista identificativi geometrie da lavorare int k = - 1 ; if ( k >= nSize - 1 || ! GetVal( vString[++k], KEY_IDS, m_vId)) return false ; for ( auto& Sel : m_vId) Sel.nId += nBaseGdbId ; // parametri lavorazione for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) { int nKey ; if ( k >= nSize - 1 || ! m_Params.FromString( vString[++k], nKey) || nKey != i) { if ( m_Params.IsOptional( i)) -- k ; else return false ; } } // parametri utensile for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i) { int nKey ; if ( k >= nSize - 1 || ! m_TParams.FromString( vString[++k], nKey) || nKey != i) return false ; } // parametri di stato while ( k < nSize - 1) { // separo chiave da valore string sKey, sVal ; SplitFirst( vString[++k], "=", sKey, sVal) ; // leggo if ( sKey == KEY_PHASE) { if ( ! FromString( sVal, m_nPhase)) return false ; } else if ( sKey == KEY_NUM) { if ( ! FromString( sVal, m_nDrillings)) return false ; } else if ( sKey == KEY_STAT) { if ( ! FromString( sVal, m_nStatus)) return false ; } else if ( sKey == KEY_SKIPS) { if ( ! FromString( sVal, m_vUndrilledId)) return false ; } } return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- Drilling::Drilling( void) { m_Params.m_sName = "*" ; m_Params.m_sToolName = "*" ; m_Params.m_sDepth = "TH" ; m_TParams.m_sName = "*" ; m_TParams.m_sHead = "*" ; m_nStatus = MCH_ST_TO_VERIFY ; m_nDrillings = 0 ; m_bTiltingTab = false ; m_bAboveHead = true ; m_bAggrBottom = false ; m_dDistBottom = 0 ; } //---------------------------------------------------------------------------- bool Drilling::Prepare( const string& sDriName) { // verifico il gestore lavorazioni if ( m_pMchMgr == nullptr) return false ; // recupero il gestore DB utensili della macchina corrente ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ; if ( pTMgr == nullptr) return false ; // recupero il gestore DB lavorazioni della macchina corrente MachiningsMgr* pMMgr = m_pMchMgr->GetCurrMachiningsMgr() ; if ( pMMgr == nullptr) return false ; // ricerca della lavorazione di libreria con il nome indicato const DrillingData* pDdata = GetDrillingData( pMMgr->GetMachining( sDriName)) ; if ( pDdata == nullptr) return false ; m_Params = *pDdata ; // ricerca dell'utensile usato dalla lavorazione const ToolData* pTdata = pTMgr->GetTool( m_Params.m_ToolUuid) ; if ( pTdata == nullptr) return false ; m_TParams = *pTdata ; m_Params.m_sToolName = m_TParams.m_sName ; return true ; } //---------------------------------------------------------------------------- bool Drilling::SetParam( int nType, bool bVal) { switch ( nType) { case MPA_INVERT : if ( bVal != m_Params.m_bInvert) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_bInvert = bVal ; return true ; } return false ; } //---------------------------------------------------------------------------- bool Drilling::SetParam( int nType, int nVal) { switch ( nType) { case MPA_SUBTYPE : if ( nVal != m_Params.m_nSubType) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_nSubType = nVal ; return true ; case MPA_SCC : if ( ! m_Params.VerifySolCh( nVal)) return false ; if ( nVal != m_Params.m_nSolCh) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_nSolCh = nVal ; return true ; } return false ; } //---------------------------------------------------------------------------- bool Drilling::SetParam( int nType, double dVal) { switch ( nType) { case MPA_SPEED : if ( ! m_TParams.VerifySpeed( dVal)) return false ; if ( abs( m_TParams.m_dSpeed - dVal) < EPS_MACH_ANG_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dSpeed) > EPS_MACH_ANG_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dSpeed = dVal ; return true ; case MPA_FEED : if ( abs( m_TParams.m_dFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dFeed = dVal ; return true ; case MPA_STARTFEED : if ( abs( m_TParams.m_dStartFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dStartFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStartFeed = dVal ; return true ; case MPA_ENDFEED : if ( abs( m_TParams.m_dEndFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dEndFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dEndFeed = dVal ; return true ; case MPA_TIPFEED : if ( abs( m_TParams.m_dTipFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dTipFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dTipFeed = dVal ; return true ; case MPA_DEPTH : { string sVal = ToString( dVal) ; if ( sVal != m_Params.m_sDepth) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sDepth = sVal ; } return true ; case MPA_STARTPOS : if ( abs( dVal - m_Params.m_dStartPos) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStartPos = dVal ; return true ; case MPA_STARTSLOWLEN : if ( abs( dVal - m_Params.m_dStartSlowLen) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStartSlowLen = dVal ; return true ; case MPA_ENDSLOWLEN : if ( abs( dVal - m_Params.m_dEndSlowLen) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dEndSlowLen = dVal ; return true ; case MPA_THROUADDLEN : if ( abs( dVal - m_Params.m_dThroughAddLen) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dThroughAddLen = dVal ; return true ; case MPA_STEP : if ( abs( dVal - m_Params.m_dStep) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStep = dVal ; return true ; case MPA_RETURNPOS : if ( abs( dVal - m_Params.m_dReturnPos) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dReturnPos = dVal ; return true ; case MPA_OVERLAP : { string sVal = ToString( dVal) ; if ( sVal != m_Params.m_sOverlap) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sOverlap = sVal ; } return true ; case MPA_STARTADDLEN : if ( abs( dVal - m_Params.m_dStartAddLen) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStartAddLen = dVal ; return true ; case MPA_ENDADDLEN : if ( abs( dVal - m_Params.m_dEndAddLen) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dEndAddLen = dVal ; return true ; case MPA_OFFSET : { string sVal = ToString( dVal) ; if ( sVal != m_Params.m_sOverlap) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sOffset = sVal ; } return true ; } return false ; } //---------------------------------------------------------------------------- bool Drilling::SetParam( int nType, const string& sVal) { switch ( nType) { case MPA_TOOL : { const ToolData* pTdata ; if ( ! m_Params.VerifyTool( m_pMchMgr->GetCurrToolsMgr(), sVal, pTdata)) return false ; if ( ! SameTool( m_TParams, *pTdata)) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sToolName = sVal ; m_Params.m_ToolUuid = pTdata->m_Uuid ; m_TParams = *pTdata ; } return true ; case MPA_DEPTH_STR : if ( sVal != m_Params.m_sDepth) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sDepth = sVal ; return true ; case MPA_SYSNOTES : if ( sVal != m_Params.m_sSysNotes) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sSysNotes = sVal ; return true ; case MPA_USERNOTES : if ( sVal != m_Params.m_sUserNotes) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sUserNotes = sVal ; return true ; case MPA_OVERLAP_STR : if ( sVal != m_Params.m_sOverlap) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sOverlap = sVal ; return true ; case MPA_OFFSET_STR : if ( sVal != m_Params.m_sOffset) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sOffset = sVal ; return true ; case MPA_INITANGS : if ( sVal != m_Params.m_sInitAngs) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sInitAngs = sVal ; return true ; case MPA_BLOCKEDAXIS : if ( sVal != m_Params.m_sBlockedAxis) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sBlockedAxis = sVal ; return true ; } return false ; } //---------------------------------------------------------------------------- bool Drilling::SetGeometry( const SELVECTOR& vIds) { // verifico validità gestore generale e gestore DB geometrico if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // copia temporanea e reset della geometria corrente SELVECTOR vOldId = m_vId ; m_vId.clear() ; // se lavorazione standard if ( m_Params.m_nSubType == DRI_SUB_STD) { // verifico che gli identificativi rappresentino dei fori (il corretto diametro è verificato dopo) for ( int i = 0 ; i < int( vIds.size()) ; ++ i) { // recupero i dati del foro Hole hole ; if ( ! GetHoleData( vIds[i], hole)) { string sInfo = "Warning in Drilling : Skipped entity " + ToString( vIds[i].nId) ; m_pMchMgr->SetWarning( 2151, sInfo) ; continue ; } // posso aggiungere alla lista m_vId.push_back( vIds[i]) ; } } // altrimenti se lavorazione lungo curve else if ( m_Params.m_nSubType == DRI_SUB_ALONG_CURVE) { // verifico che gli identificativi rappresentino delle curve for ( int i = 0 ; i < int( vIds.size()) ; ++ i) { if ( ( m_pGeomDB->GetGeoType( vIds[i].nId) & GEO_CURVE) == 0) { string sInfo = "Warning in Drilling : Skipped entity " + ToString( vIds[i].nId) ; m_pMchMgr->SetWarning( 2151, sInfo) ; continue ; } // posso aggiungere alla lista m_vId.push_back( vIds[i]) ; } } // aggiorno lo stato if ( m_vId != vOldId) m_nStatus |= MCH_ST_GEO_MODIF ; // restituisco presenza geometria da lavorare return ( ! m_vId.empty()) ; } //---------------------------------------------------------------------------- bool Drilling::Preview( bool bRecalc) { // reset numero forature nella lavorazione m_nDrillings = 0 ; // reset fori non lavorati m_vUndrilledId.clear() ; // verifico validità gestore DB geometrico e Id del gruppo if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId)) return false ; // recupero gruppo per geometria di Preview int nPvId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_PV) ; // se non c'è, lo aggiungo if ( nPvId == GDB_ID_NULL) { nPvId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nPvId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPvId, MCH_PV) ; } // altrimenti lo lavoro else m_pGeomDB->EmptyGroup( nPvId) ; // aggiorno dati geometrici dell'utensile if ( ! UpdateToolData()) { m_pMchMgr->SetLastError( 2101, "Error in Drilling : UpdateToolData failed") ; return false ; } // se lavorazione standard if ( m_Params.m_nSubType == DRI_SUB_STD) return StandardProcess( bRecalc, nPvId, GDB_ID_NULL) ; // se altrimenti lavorazione lungo curve else if ( m_Params.m_nSubType == DRI_SUB_ALONG_CURVE) return AlongCurveProcess( bRecalc, nPvId, GDB_ID_NULL) ; // altrimenti errore else return false ; } //---------------------------------------------------------------------------- bool Drilling::Apply( bool bRecalc, bool bPostApply) { // reset numero forature nella lavorazione int nCurrDrillings = m_nDrillings ; m_nDrillings = 0 ; // reset fori non lavorati INTVECTOR vCurrUndrilledId = m_vUndrilledId ; m_vUndrilledId.clear() ; // verifico validità gestore DB geometrico e Id del gruppo if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId)) return false ; // aggiorno dati geometrici dell'utensile if ( ! UpdateToolData()) { m_pMchMgr->SetLastError( 2101, "Error in Drilling : UpdateToolData failed") ; return false ; } // se modificata geometria, necessario ricalcolo if ( ( m_nStatus & MCH_ST_GEO_MODIF) != 0) bRecalc = true ; // verifico se necessario continuare nell'aggiornamento if ( ! bRecalc && ( m_nStatus == MCH_ST_OK || m_nStatus == MCH_ST_NO_POSTAPPL)) { // confermo i percorsi di lavorazione e i fori non lavorati m_nDrillings = nCurrDrillings ; m_vUndrilledId = vCurrUndrilledId ; string sLog = string( "Drilling apply skipped : status ") + ( m_nStatus == MCH_ST_OK ? "already ok" : "no postapply") ; LOG_DBG_INFO( GetEMkLogger(), sLog.c_str()) ; // eseguo aggiornamento assi macchina e collegamento con operazione precedente if ( ! Update( bPostApply)) return false ; m_nStatus = ( bPostApply ? MCH_ST_OK : MCH_ST_NO_POSTAPPL) ; LOG_DBG_INFO( GetEMkLogger(), "Update done") ; // esco con successo return true ; } m_nStatus = MCH_ST_TO_VERIFY ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_CL) ; // se non c'è, lo aggiungo if ( nClId == GDB_ID_NULL) { nClId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nClId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nClId, MCH_CL) ; } // altrimenti lo lavoro else m_pGeomDB->EmptyGroup( nClId) ; // elimino eventuale gruppo geometria simmetrica per lavorazione in doppio int nDblId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_DBL) ; if ( nDblId != GDB_ID_NULL) { m_pGeomDB->Erase( nDblId) ; nDblId = GDB_ID_NULL ; } // rendo corrente l'utensile usato nella lavorazione if ( ! m_pMchMgr->SetCalcTool( m_TParams.m_sName, m_TParams.m_sHead, m_TParams.m_nExit)) { m_pMchMgr->SetLastError( 2118, "Error in Drilling : Tool loading failed") ; return false ; } // verifico se tavola basculante bool bTiltTab = false ; m_bTiltingTab = ( m_pMchMgr->GetCurrMachine()->GetCurrTableIsTilting( bTiltTab, m_vtTiltingAx) && bTiltTab) ; // verifico se testa da sopra (Z+) m_bAboveHead = m_pMchMgr->GetHeadAbove( m_TParams.m_sHead) ; // se lavorazione standard if ( m_Params.m_nSubType == DRI_SUB_STD) { if ( ! StandardProcess( bRecalc, GDB_ID_NULL, nClId)) return false ; } // se altrimenti lavorazione lungo curve else if ( m_Params.m_nSubType == DRI_SUB_ALONG_CURVE) { if ( ! AlongCurveProcess( bRecalc, GDB_ID_NULL, nClId)) return false ; } // altrimenti errore else return false ; // assegno ingombri dei vari percorsi di lavorazione e della lavorazione nel suo complesso CalcAndSetBBox( nClId) ; // se lavorazione in doppio, aggiungo geometria della parte simmetrica if ( ! CalcMirrorByDouble( nClId, m_Params.m_sUserNotes)) { m_pMchMgr->SetLastError( 2115, "Error in Drilling : Mirror for Double calculation failed") ; return false ; } // eseguo aggiornamento assi macchina e collegamento con operazione precedente if ( ! Update( bPostApply)) return false ; // aggiorno stato della lavorazione m_nStatus = ( bPostApply ? MCH_ST_OK : MCH_ST_NO_POSTAPPL) ; // dichiaro successiva da aggiornare UpdateFollowingOperationsStatus( MCH_ST_OTH_MODIF) ; LOG_DBG_INFO( GetEMkLogger(), "Drilling apply done") ; return true ; } //---------------------------------------------------------------------------- bool Drilling::Update( bool bPostApply) { // verifico validità gestore DB geometrico e Id del gruppo if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId)) return false ; // se lavorazione vuota, esco if ( m_nDrillings == 0) { m_pMchMgr->SetWarning( 2152, "Warning in Drilling : No machinable hole") ; return true ; } // elimino le entità CLIMB, RISE e HOME della lavorazione, potrebbero falsare i calcoli degli assi (in ogni casi vengono riaggiunte dopo) RemoveClimbRiseHome() ; // imposto eventuale asse bloccato da lavorazione SetBlockedRotAxis( m_Params.m_sBlockedAxis) ; // calcolo gli assi macchina string sHint = ExtractHint( m_Params.m_sUserNotes) ; if ( ! m_Params.m_sInitAngs.empty()) sHint = m_Params.m_sInitAngs ; if ( ! CalculateAxesValues( sHint)) { string sInfo = m_pMchMgr->GetOutstrokeInfo() ; if ( sInfo.empty()) m_pMchMgr->SetLastError( 2109, "Error in Drilling : axes values not calculable") ; else m_pMchMgr->SetLastError( 2110, "Error in Drilling : outstroke ") ; return false ; } // assegno estremi degli assi dei vari percorsi di lavorazione e della lavorazione nel suo complesso CalcAndSetAxesBBox() ; // se lavorazione in doppio, calcolo assi e movimenti di approccio e retrazione relativi if ( GetDoubleType( m_Params.m_sUserNotes) != 0) { // elimino le entità CLIMB, RISE e HOME della lavorazione in doppio RemoveClimbRiseHome( false) ; // recupero i dati della testa in doppio e la imposto string sDblTool ; string sDblTcPos ; string sDblHead ; int nDblExit ; bool bOk = GetDoubleToolData( sDblTool, sDblTcPos, sDblHead, nDblExit) && m_pMchMgr->SetCalcTool( sDblTool, sDblHead, nDblExit) ; // imposto eventuale asse bloccato da lavorazione SetBlockedRotAxis( m_Params.m_sBlockedAxis, true) ; // eseguo il calcolo if ( bOk) { if ( ! CalculateDoubleAxesValues( sHint)) { string sInfo = m_pMchMgr->GetOutstrokeInfo() ; if ( sInfo.empty()) m_pMchMgr->SetLastError( 2109, "Error in Drilling : axes values not calculable for double") ; else m_pMchMgr->SetLastError( 2110, "Error in Drilling : double outstroke ") ; bOk = false ; } } // ripristino testa principale m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr()) ; // in caso di errore, esco if ( ! bOk) return false ; } // esecuzione eventuali personalizzazioni speciali string sSpecErr ; if ( bPostApply && ! SpecialApply( sSpecErr)) { if ( ! IsEmptyOrSpaces( sSpecErr)) m_pMchMgr->SetLastError( 2119, sSpecErr) ; else m_pMchMgr->SetLastError( 2119, "Error in Drilling : special apply not calculable") ; return false ; } // gestione movimenti all'inizio di ogni singolo percorso di lavorazione e alla fine della lavorazione if ( ! AdjustStartEndMovements()) { string sInfo = m_pMchMgr->GetOutstrokeInfo() ; if ( sInfo.empty()) m_pMchMgr->SetLastError( 2111, "Error in Drilling : link movements not calculable") ; else m_pMchMgr->SetLastError( 2112, "Error in Drilling : link outstroke ") ; return false ; } // esecuzione eventuali personalizzazioni string sPostErr ; if ( bPostApply && ! PostApply( sPostErr)) { if ( ! IsEmptyOrSpaces( sPostErr)) m_pMchMgr->SetLastError( 2113, sPostErr) ; else m_pMchMgr->SetLastError( 2113, "Error in Drilling : post apply not calculable") ; return false ; } return true ; } //---------------------------------------------------------------------------- int Drilling::VerifyMultiParallelDrills( void) { // se aggregato da sotto, sicuramente con una sola punta bool bAggrBottom = IsAggrBottom( m_TParams.m_sHead) ; if ( bAggrBottom) return DRILL_TYPE_STD ; // se una sola uscita, inutile continuare int nExitCnt = m_pMchMgr->GetCurrMachine()->GetHeadExitCount( m_TParams.m_sHead) ; if ( nExitCnt == 1) return DRILL_TYPE_STD ; // verifico che le uscite siano fisse int nSelectType = m_pMchMgr->GetCurrMachine()->GetHeadSelectType( m_TParams.m_sHead) ; if ( nSelectType != MCH_SLT_FIXEDEXITS && nSelectType != MCH_SLT_MULTIEXITS) return DRILL_TYPE_STD ; // recupero la direzione dell'utensile principale Point3d ptMainExit ; Vector3d vtMainTool, vtMainAux ; m_pMchMgr->GetCurrMachine()->GetHeadExitPosDirAux( m_TParams.m_sHead, m_TParams.m_nExit, ptMainExit, vtMainTool, vtMainAux) ; // verifico ci sia almeno un'altra uscita attrezzata parallela a quella principale for ( int nT = 0 ; nT < nExitCnt ; ++ nT) { if ( nT + 1 == m_TParams.m_nExit) continue ; string sToolName ; if ( m_pMchMgr->GetLoadedTool( m_TParams.m_sHead, nT + 1, sToolName) && ! sToolName.empty()) { // recupero la tipologia di utensile int nType = TT_NONE ; string sCurrTool ; m_pMchMgr->TdbGetCurrToolParam( TPA_NAME, sCurrTool) ; if ( m_pMchMgr->TdbSetCurrTool( sToolName)) { m_pMchMgr->TdbGetCurrToolParam( TPA_TYPE, nType) ; if ( ! IsEmptyOrSpaces( sCurrTool)) m_pMchMgr->TdbSetCurrTool( sCurrTool) ; } // verifico punta o fresa con direzione corretta Point3d ptExit ; Vector3d vtTool, vtAux ; if ( ( ( nType & TF_DRILLBIT) != 0 || nType == TT_MILL_STD) && m_pMchMgr->GetCurrMachine()->GetHeadExitPosDirAux( m_TParams.m_sHead, nT + 1, ptExit, vtTool, vtAux) && AreSameVectorApprox( vtTool, vtMainTool)) { if ( nSelectType == MCH_SLT_FIXEDEXITS) return DRILL_TYPE_MULTI_FIXED ; else return DRILL_TYPE_MULTI_SEL ; } } } // non è stato trovato niente return ( nSelectType == MCH_SLT_MULTIEXITS ? DRILL_TYPE_MULTI_SEL : DRILL_TYPE_STD) ; } //---------------------------------------------------------------------------- bool Drilling::StandardProcess( bool bRecalc, int nPvId, int nClId) { // recupero il tipo di lavorazione int nDrillType = VerifyMultiParallelDrills() ; // eseguo la lavorazione richiesta switch ( nDrillType) { case DRILL_TYPE_STD : { // se vtAux impostato come parametro di lavorazione nelle UserNotes Vector3d vtAux ; if ( GetValInNotes( m_Params.m_sUserNotes, UN_VTAUXDIR, vtAux)) vtAux.Normalize() ; // elaboro i singoli fori for ( int i = 0 ; i < int( m_vId.size()) ; ++ i) { const auto& vId = m_vId[i] ; // se richiesto preview if ( nPvId != GDB_ID_NULL) { if ( ! GenerateHolePv( i, vId, MCH_PATH, nPvId)) return false ; // creo la regione di ingombro del foro int nDriId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetLastGroupInGroup( nPvId)) ; GenerateHoleRegionPv( nDriId, 1, nPvId) ; } // se richiesta lavorazione if ( nClId != GDB_ID_NULL) { if ( ! GenerateHoleCl( i, vId, MCH_PATH, nClId, 0, vtAux)) return false ; } } } break ; case DRILL_TYPE_MULTI_FIXED : { TABMHDRILL tabDrills ; double dMHOff = 0 ; if ( ! MultiHeadDrilling( m_vId, nClId, true, tabDrills, dMHOff)) return false ; if ( tabDrills.empty()) return true ; for ( int i = 0 ; i < int( tabDrills.size()) ; ++ i) { // se richiesto preview if ( nPvId != GDB_ID_NULL) { if ( ! GenerateHolePv( i, m_vId[tabDrills[i].nHoleInd], MCH_PATH, nPvId)) return false ; // creo la regione di ingombro del foro int nDriId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetLastGroupInGroup( nPvId)) ; GenerateHoleRegionPv( nDriId, 1, nPvId) ; } // se richiesta lavorazione if ( nClId != GDB_ID_NULL) { if ( ! GenerateHoleCl( i, m_vId[tabDrills[i].nHoleInd], MCH_PATH, nClId, dMHOff, tabDrills[i].vtAux, DRILL_TYPE_MULTI_FIXED)) return false ; } } } break ; case DRILL_TYPE_MULTI_SEL : { // eseguo il MultiHead drilling per gruppo a forare TABMHDRILL tabDrills ; double dMHOff = 0 ; if ( ! MultiHeadDrilling( m_vId, nClId, false, tabDrills, dMHOff)) return false ; if ( tabDrills.empty()) return true ; for ( int i = 0 ; i < int( tabDrills.size()) ; ++ i) { // se richiesto preview if ( nPvId != GDB_ID_NULL) { if ( ! GenerateHolePv( i, m_vId[tabDrills[i].nHoleInd], MCH_PATH, nPvId)) return false ; // creo la regione di ingombro del foro int nDriId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetLastGroupInGroup( nPvId)) ; GenerateHoleRegionPv( nDriId, 1, nPvId) ; } // se richiesta lavorazione if ( nClId != GDB_ID_NULL) { #if ENABLE_DEBUG_MULTIHEAD_HOLES PrintDescent( tabDrills[i]) ; #endif if ( ! GenerateHoleCl( i, m_vId[tabDrills[i].nHoleInd], MCH_PATH, nClId, dMHOff, tabDrills[i].vtAux, DRILL_TYPE_MULTI_SEL, &( tabDrills[i].vActiveExit), &( tabDrills[i].currTool))) return false ; } } } break ; case DRILL_TYPE_ERR : default : return false ; break ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::CalcDrilledHolesByConfig( VECTORHOLE& vHoles, int nMyInd, int nIndConfig, INTVECTOR& vIndDrilled) { /* restituisce quali fori non ancora lavorati vengono lavorati con la configurazione di indice nIndConfig per il foro nMyInd-esimo */ // controllo dei parametri if ( nMyInd < 0 || nMyInd >= int( vHoles.size()) || nIndConfig < 0 || nIndConfig >= int( vHoles[nMyInd].vIndTools.size())) return false ; vIndDrilled.clear() ; if ( vHoles.empty()) return true ; // dichiarazione variabili di supporto int nMainHole = vHoles[nMyInd].vToolHole[nIndConfig] ; Vector3d vtMainAux = vHoles[nMyInd].vVtAux[nIndConfig] ; // scorro tutti i fori for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { // se foro i-esimo già lavorato, passo al successivo if ( vHoles[i].bDrill) continue ; // se il foro i-esimo è il corrente, lo inserisco ( è lavorato in quanto nella configurazione) if ( i == nMyInd) { vIndDrilled.emplace_back( i) ; continue ; } // se il foro i-esimo è quello in cui il tool è posizionato, lo inserisco ( idem) else if ( i == nMainHole) { vIndDrilled.emplace_back( i) ; continue ; } else { // se foro i-esimo viene lavorato mettendo il tool principale nel foro nMainHole-esimo e con lo // stesso orientamento della configurazione corrente, inserisco il foro for ( int j = 0 ; j < int( vHoles[i].vToolHole.size()) ; ++ j) { if ( vHoles[i].vToolHole[j] == nMainHole && AreSameVectorApprox( vHoles[i].vVtAux[j], vtMainAux)) vIndDrilled.emplace_back( i) ; } } } return true ; } //---------------------------------------------------------------------------- bool Drilling::GetClosestHolesToHole( const VECTORHOLE& vHoles, int nMyInd, bool bDrilled, INTVECTOR& vInds) { /* funzione che restutisce gli indici dei fori Drilled/nonDrilled più vicini al foro nMyInd-esimo */ // controllo dei parametri if ( nMyInd < 0 || nMyInd >= int( vHoles.size())) return false ; vInds.clear() ; if ( vHoles.empty()) return true ; // scorro i fori double dMinDist = INFINITO ; for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { // escludo il foro nInd-esimo if ( i == nMyInd) continue ; // se foro di tipologia opposta, passo al successivo if ( vHoles[i].bDrill != bDrilled) continue ; // se il foro non è lavorabile, non lo conto bool bUndrillable = true ; for ( int j = 0 ; j < int( vHoles[i].vIndTools.size()) && bUndrillable ; ++ j) { bUndrillable = ( vHoles[i].vToolHole[j] == IND_TOOL_INVALID && vHoles[i].vIndTools[j] == IND_TOOL_INVALID && ! vHoles[i].vVtAux[j].IsValid()) ; } if ( bUndrillable) continue ; // calcolo la distanza tra i due fori double dSqDist = SqDist( vHoles[nMyInd].hole.ptIni, vHoles[i].hole.ptIni) ; if ( dSqDist < dMinDist) { dMinDist = dSqDist ; vInds.clear() ; vInds.emplace_back( i) ; } else if ( abs( dSqDist - dMinDist) < 100 * SQ_EPS_SMALL) vInds.emplace_back( i) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::OrderConfigsForSelectableTools( VECTORHOLE& vHoles, const VECTORTOOL& vTools, int nIndToolMain, TABMHDRILL& vDrills) { // se non ho fori, allora non faccio nulla vDrills.clear() ; if ( vHoles.empty()) return true ; // recupero gli indici dei fori non lavorabili INTVECTOR vIndUndrilledHoles ; for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { // scorro sulle configurazioni trovate bool bIsUndrilled = true ; for ( int j = 0 ; j < int( vHoles[i].vIndTools.size()) && bIsUndrilled ; ++ j) bIsUndrilled = ( vHoles[i].vIndTools[j] == IND_TOOL_INVALID && vHoles[i].vToolHole[j] == IND_TOOL_INVALID && ! vHoles[i].vVtAux[j].IsValid()) ; // se foro non lavorabile, lo inserisco nel vettore if ( bIsUndrilled) vIndUndrilledHoles.emplace_back( i) ; } // recupero il numero di fori totali int nHoles = int( vHoles.size()) ; // recupero il numero di fori non lavorabili int nUndrilledHoles = int( vIndUndrilledHoles.size()) ; // se i due valori coincidono, allora ho finito if ( nHoles == nUndrilledHoles) return true ; // considero tutti i fori come non lavorati for ( int i = 0 ; i < nHoles ; ++ i) vHoles[i].bTempDrill = false ; // inizializzo contatore di fori lavorati int nDrilledHoles = 0 ; #if ENABLE_DEBUG_MULTIHEAD_HOLES LOG_ERROR( GetEMkLogger(), "--- Configurazioni scelte : ") ; #endif // finchè non lavoro tutti i fori ammissibili const int MAXTRY = 5000 ; int nCont = 0 ; while ( nDrilledHoles < nHoles - nUndrilledHoles && nCont < MAXTRY) { // cerco la(e) configurazione(i) che lavora(no) più fori INTVECTOR vIndBestConfigs ; GetConfigsWithMoreDrilledHoles( vHoles, vIndBestConfigs) ; // se non ho trovato nessuna configurazione, allora non faccio nulla if ( vIndBestConfigs.empty()) break ; int nBestConfig = vIndBestConfigs[0] ; ChooseBestConfigForSelectableTools( vHoles, nIndToolMain, vIndBestConfigs, nBestConfig) ; // imposto bTempDrill per i fori in esame INTVECTOR vIndActiveExits ; int nRefHole = 0 ; for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { if ( ! vHoles[i].bTempDrill && vHoles[i].vIndTools[nBestConfig] != IND_TOOL_INVALID && vHoles[i].vToolHole[nBestConfig] != IND_TOOL_INVALID && vHoles[i].vVtAux[nBestConfig].IsValid()) { // flag per lavorazione temporanea vHoles[i].bTempDrill = true ; // aumento contatore fori lavorati ++ nDrilledHoles ; // ricavo la testa su cui è montato l'utensile vIndActiveExits.emplace_back( vTools[vHoles[i].vIndTools[nBestConfig]].pTool->m_nExit) ; // memorizzo un foro di riferimento nRefHole = i ; } } // calcolo la maschera per la configurazione attuale INTINTVECTOR vMyMask ; if ( ! MultiHeadHoleToolsConfig( vHoles, nBestConfig, vMyMask)) return false ; // inserisco la configurazione per la lavorazione vDrills.emplace_back( vHoles[nRefHole].vToolHole[nBestConfig], vHoles[nRefHole].vVtAux[nBestConfig], vIndActiveExits, vTools[vHoles[nRefHole].vIndMainTool[nBestConfig]], vMyMask) ; #if ENABLE_DEBUG_MULTIHEAD_HOLES string sRow = " (" ; sRow.append( ToString( nBestConfig)) ; sRow.append( ") --> uscite attive : ") ; for ( int _a = 0 ; _a < int( vIndActiveExits.size()) ; ++ _a) { sRow.append( ToString( vIndActiveExits[_a])) ; if ( _a != int( vIndActiveExits.size()) - 1) sRow.append( ",") ; } sRow.append( " [#") ; sRow.append( ToString( int( vIndActiveExits.size()))) ; sRow.append( "]. -> Corrispondenze :") ; for ( int _a = 0 ; _a < int( vMyMask.size()) ; ++ _a) sRow.append( " H:" + ToString( vMyMask[_a].first) + "|T:" + ToString( vMyMask[_a].second)) ; LOG_ERROR( GetEMkLogger(), sRow.c_str()) ; #endif // incremento contatore tentativi massimi ++ nCont ; } // se ho superato i tentativi massimi, errore if ( nCont == MAXTRY) { vDrills.clear() ; return false ; } // Riordino le configurazioni trovate if ( vDrills.empty() || int( vDrills.size()) == 1) return true ; Point3d ptRef = ORIG ; INTVECTOR vIndToOrder ; vIndToOrder.reserve( vDrills.size()) ; vector> vIntervals ; int nIndS = 0 ; int nCurrSize = -1 ; for ( int i = 0 ; i < int( vDrills.size()) ; ++ i) { if ( i == 0) { nCurrSize = int( vDrills[i].vActiveExit.size()) ; continue ; } if ( int( vDrills[i].vActiveExit.size()) == nCurrSize) { if ( i == ( vDrills.size() - 1)) vIntervals.emplace_back( make_pair( nIndS, i + 1)) ; continue ; } else { vIntervals.emplace_back( make_pair( nIndS, i)) ; nIndS = i ; nCurrSize = int( vDrills[i].vActiveExit.size()) ; -- i ; } } #if ENABLE_DEBUG_MULTIHEAD_HOLES string sRow = "---- Raggruppamento #uscite --- " ; for ( int _a = 0 ; _a < int( vIntervals.size()) ; ++ _a) { sRow.append( "(") ; sRow.append( ToString( vIntervals[_a].first)) ; sRow.append( ",") ; sRow.append( ToString( vIntervals[_a].second)) ; sRow.append( ")") ; if ( _a != int( vIntervals.size()) - 1) sRow.append( " | ") ; } LOG_ERROR( GetEMkLogger(), sRow.c_str()) ; #endif for ( int i = 0 ; i < int( vIntervals.size()) ; ++ i) { for ( int j = vIntervals[i].first ; j < vIntervals[i].second ; ++ j) { vector> my_vOrder ; for ( int k = vIntervals[i].first ; k < vIntervals[i].second ; ++ k) { if ( find( vIndToOrder.begin(), vIndToOrder.end(), k) != vIndToOrder.end()) continue ; my_vOrder.push_back( make_pair( k, SqDist( vHoles[vDrills[k].nHoleInd].hole.ptIni, ptRef))) ; } double dMinDist = INFINITO ; int nMyIndex = 0 ; for ( auto& _order : my_vOrder) { if ( _order.second < dMinDist) { nMyIndex = _order.first ; ptRef = vHoles[vDrills[nMyIndex].nHoleInd].hole.ptIni ; dMinDist = _order.second ; } } vIndToOrder.push_back( nMyIndex) ; } } #if ENABLE_DEBUG_MULTIHEAD_HOLES string sRow1 = "---- Ordine Indici condifugrazioni --- " ; for ( int _a = 0 ; _a < int( vIndToOrder.size()) ; ++ _a) { sRow1.append( ToString( vIndToOrder[_a])) ; if ( _a != int( vIndToOrder.size()) - 1) sRow1.append( " | ") ; } LOG_ERROR( GetEMkLogger(), sRow1.c_str()) ; #endif TABMHDRILL vOrderedDrill ; for ( size_t i = 0 ; i < vIndToOrder.size() ; ++ i) vOrderedDrill.emplace_back( vDrills[vIndToOrder[i]]) ; swap( vDrills, vOrderedDrill) ; return true ; } //---------------------------------------------------------------------------- bool Drilling::ChooseBestConfigForSelectableTools( const VECTORHOLE& vHoles, int nIndToolMain, INTVECTOR& vConfInds, int& nBestConf) { /* algoritmo Greedy per la scelta della miglior configurazione per tool selezionabili */ // se non ho fori, allora ho finito if ( vHoles.empty()) return true ; // se non ho configurazioni, allora ho finito if ( vConfInds.empty()) return true ; // per sicurezza la prima configurazione è la migliore ( decisione temporanea ) nBestConf = vConfInds[0] ; // se ho una sola configurazione, allora è quella ottimale if ( int( vConfInds.size()) == 1) return true ; // euristicamente considero accettabili solo le configurazioni dove compare il tool principale // ( se non la trovo scelgo semplicemente la prima ) INTVECTOR vConfInds_Greedy0 ; for ( int nC = 0 ; nC < int( vConfInds.size()) ; ++ nC) { int nMyConfig = vConfInds[nC] ; for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { if ( ! vHoles[i].bTempDrill && vHoles[i].vIndMainTool[nMyConfig] == nIndToolMain && vHoles[i].vToolHole[nMyConfig] != IND_TOOL_INVALID && vHoles[i].vVtAux[nMyConfig].IsValid()) { vConfInds_Greedy0.emplace_back( vConfInds[nC]) ; break ; } } } // se ho meno di due configurazioni, ho trovato quella "ottimale" if ( int( vConfInds_Greedy0.size()) < 2) { if ( ! vConfInds_Greedy0.empty()) nBestConf = vConfInds_Greedy0[0] ; return true ; } // cerco tra le configurazioni trovate, quella che inserisce il tool main in un foro non lavorato INTVECTOR vConfInds_Greedy1 ; for ( int nC = 0 ; nC < int( vConfInds_Greedy0.size()) ; ++ nC) { int nMyConfig = vConfInds_Greedy0[nC] ; for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { if ( ! vHoles[i].bTempDrill && vHoles[i].vIndMainTool[nMyConfig] == nIndToolMain && vHoles[i].vToolHole[nMyConfig] == i && vHoles[i].vVtAux[nMyConfig].IsValid()) { vConfInds_Greedy1.emplace_back( vConfInds_Greedy0[nC]) ; break ; } } } // se ho meno di due configurazioni, ho trovato quella "ottimale" if ( int( vConfInds_Greedy1.size()) < 2) { if ( ! vConfInds_Greedy1.empty()) nBestConf = vConfInds_Greedy1[0] ; return true ; } // ... servono altri test di ottimizzazione ? ... nBestConf = vConfInds_Greedy1[0] ; return true ; } //---------------------------------------------------------------------------- bool Drilling::GetConfigsWithMoreDrilledHoles( const VECTORHOLE& vHoles, INTVECTOR& vInds) { /* controllando il parambetro bTempDrilled, la funzione restituisce la configurazione ( le configurazioni ) che svuotano il maggior numero di fori */ // se non ho fori, allora non faccio nulla vInds.clear() ; if ( vHoles.empty()) return true ; // scrorro le configurazioni presenti int nMaxDrilled = 0 ; for ( int nC = 0 ; nC < int( vHoles[0].vToolHole.size()) ; ++ nC) { // scorro i fori int nCurrDrilled = 0 ; for ( int nH = 0 ; nH < int( vHoles.size()) ; ++ nH) { // se il foro è già lavorato, passo al successivo if ( vHoles[nH].bTempDrill) continue ; // controllo se il foro non lavorato ha una configurazione valida if ( vHoles[nH].vToolHole[nC] != IND_TOOL_INVALID && vHoles[nH].vIndTools[nC] != IND_TOOL_INVALID && vHoles[nH].vVtAux[nC].IsValid()) ++ nCurrDrilled ; } if ( nCurrDrilled > nMaxDrilled) nMaxDrilled = nCurrDrilled ; } // se non ho trovato configurazioni, vuol dire che tutti i fori sono lavorati o che non // esiste una configurazione ammissibile if ( nMaxDrilled == 0) return true ; // cerco tutte le configurazioni che svuotano nMaxDrilled fori for ( int nC = 0 ; nC < int( vHoles[0].vToolHole.size()) ; ++ nC) { // scorro i fori int nCurrDrilled = 0 ; for ( int nH = 0 ; nH < int( vHoles.size()) ; ++ nH) { // se il foro è già lavorato, passo al successivo if ( vHoles[nH].bTempDrill) continue ; // controllo se il foro non lavorato ha una configurazione valida if ( vHoles[nH].vToolHole[nC] != IND_TOOL_INVALID && vHoles[nH].vIndTools[nC] != IND_TOOL_INVALID && vHoles[nH].vVtAux[nC].IsValid()) ++ nCurrDrilled ; } if ( nCurrDrilled == nMaxDrilled) vInds.emplace_back( nC) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::EraseDuplicatedConfigs( VECTORHOLE& vHoles) { /* funzione per rimuovere le configurazioni tra loro equivalenti */ // scorro il vettore dei fori if ( vHoles.empty()) return true ; // analizzo il primo foro selezionato e scorro le configurazioni for ( int nC = 0 ; nC < int( vHoles[0].vIndTools.size()) - 1 ; ++ nC) { for ( int nC1 = nC + 1 ; nC1 < int( vHoles[0].vToolHole.size()) ; ++ nC1) { if ( vHoles[0].vToolHole[nC] == vHoles[0].vToolHole[nC1] && vHoles[0].vIndTools[nC] == vHoles[0].vIndTools[nC1] && ( AreSameVectorApprox( vHoles[0].vVtAux[nC], vHoles[0].vVtAux[nC1]) || ( ! vHoles[0].vVtAux[nC].IsValid() && ! vHoles[0].vVtAux[nC1].IsValid()))) { // controllo se vale anche per gli altri fori bool bSame = true ; for ( int i = 1 ; i < int( vHoles.size()) && bSame ; ++ i) { bSame = ( vHoles[i].vToolHole[nC] == vHoles[i].vToolHole[nC1] && vHoles[i].vIndTools[nC] == vHoles[i].vIndTools[nC1] && ( AreSameVectorApprox( vHoles[i].vVtAux[nC], vHoles[i].vVtAux[nC1]) || ( ! vHoles[i].vVtAux[nC].IsValid() && ! vHoles[i].vVtAux[nC1].IsValid()))) ; } if ( bSame) { for ( int k = 0 ; k < int( vHoles.size()) ; ++ k) { vHoles[k].vToolHole.erase( vHoles[k].vToolHole.begin() + nC1) ; vHoles[k].vIndTools.erase( vHoles[k].vIndTools.begin() + nC1) ; vHoles[k].vVtAux.erase( vHoles[k].vVtAux.begin() + nC1) ; } -- nC1 ; } } } } return true ; } //---------------------------------------------------------------------------- bool Drilling::KeepMinRotatedConfigs( VECTORHOLE& vHoles, const Vector3d& vtAux, const Vector3d& vtTool) { /* riduco il numero di cobinazioni in base a vtAux */ // se non ho fori, allora esco if ( vHoles.empty()) return true ; // scorro il vettore dei fori for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { Vector3d vtMinAndDegAux ; double dMinAngDeg = ANG_FULL ; for ( int j = 0 ; j < int( vHoles[i].vVtAux.size()) ; ++ j) { // se non valido, passo al successivo if ( ! vHoles[i].vVtAux[j].IsValid()) continue ; // calcolo l'angolo con segno della configurazione rispetto a vtAux double dAngDeg ; bool bDet ; if ( ! vHoles[i].vVtAux[j].GetRotation( vtAux, vtTool, dAngDeg, bDet)) return false ; // conservo l'angolo minimo if ( abs( dAngDeg) < dMinAngDeg) { dMinAngDeg = abs( dAngDeg) ; vtMinAndDegAux = vHoles[i].vVtAux[j] ; } } for ( int j = 0 ; j < int( vHoles[i].vVtAux.size()) ; ++ j) { // se vettore ausiliario corrente diverso da quello minimo e non presente tra le configurazioni // valide, allora rendo invalida la configurazione if ( vHoles[i].vVtAux[j].IsValid() && ! AreSameOrOppositeVectorApprox( vHoles[i].vVtAux[j], vtMinAndDegAux)) { vHoles[i].vToolHole[j] = IND_TOOL_INVALID ; vHoles[i].vIndTools[j] = IND_TOOL_INVALID ; vHoles[i].vVtAux[j] = V_INVALID ; } } } // cancello tutte le configurazioni invalide int nSize = int( vHoles[0].vIndTools.size()) ; for ( int j = 0 ; j < nSize ; ++ j) { bool bInvalid = true ; for ( int i = 0 ; i < int( vHoles.size()) && bInvalid ; ++ i) bInvalid = ( vHoles[i].vIndTools[j] == IND_TOOL_INVALID && vHoles[i].vToolHole[j] == IND_TOOL_INVALID && ! vHoles[i].vVtAux[j].IsValid()) ; if ( bInvalid) { for ( int k = 0 ; k < int( vHoles.size()) ; ++ k) { vHoles[k].vToolHole.erase( vHoles[k].vToolHole.begin() + j) ; vHoles[k].vIndTools.erase( vHoles[k].vIndTools.begin() + j) ; vHoles[k].vVtAux.erase( vHoles[k].vVtAux.begin() + j) ; } ; -- nSize ; -- j ; } } return true ; } //---------------------------------------------------------------------------- bool Drilling::CalcMultiHeadUndrilledHoles( const VECTORHOLE& vHoles, INTVECTOR& vIdUndrilledHoles) { /* funzione per calcolare gli Id dei fori fori non lavorati */ // se non ho fori, ho finito if ( vHoles.empty()) return true ; // scorro sui fori for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { // scorro sulle configurazioni trovate bool bIsUndrilled = true ; for ( int j = 0 ; j < int( vHoles[i].vIndTools.size()) && bIsUndrilled ; ++ j) bIsUndrilled = ( vHoles[i].vIndTools[j] == IND_TOOL_INVALID && vHoles[i].vToolHole[j] == IND_TOOL_INVALID && ! vHoles[i].vVtAux[j].IsValid()) ; // se foro non lavorabile, lo inserisco nel vettore if ( bIsUndrilled) vIdUndrilledHoles.emplace_back( vHoles[i].hole.nOriId) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::CalcMultiHeadPartialDrilledHoles( const TABMHDRILL& vDrills, const VECTORHOLE& vHoles, const VECTORTOOL& vTools, const INTVECTOR& vIdUndrilledHoles, INTDBLVECTOR& vIdPartialdrilledHoles) { // se non ho configurazioni, allora non devo fare nulla vIdPartialdrilledHoles.clear() ; if ( vDrills.empty()) return true ; // tutti i fori non lavorabili non sono parzialmente lavorati vector> vbPartDrill( vHoles.size(), make_pair( true, INFINITO)) ; for ( int nH = 0 ; nH < int( vHoles.size()) && ! vIdUndrilledHoles.empty() ; ++ nH) { if ( find( vIdUndrilledHoles.begin(), vIdUndrilledHoles.end(), vHoles[nH].hole.nOriId) != vIdUndrilledHoles.end()) vbPartDrill[nH].first = false ; // non potrà essere lavorato parzialmente... } // scorro le configurazioni for ( int nC = 0 ; nC < int( vDrills.size()) ; ++ nC) { // scorro i fori che compaiono nella configurazione nC-esima for ( int nH = 0 ; nH < int( vDrills[nC].vMatMask.size()) ; ++ nH) { int nIndH = vDrills[nC].vMatMask[nH].first ; int nIndT = vDrills[nC].vMatMask[nH].second ; // controllo per estrema sicurezza che gli indici siano corretti (_controllo_per_sicurezza_(debug)) if ( nIndH < 0 || nIndH >= int( vHoles.size()) || nIndT < 0 || nIndT >= int( vTools.size())) return false ; // non dovrei capitare qui... // se foro già lavorato completamente, non lo controllo, passo al foro successivo if ( ! vbPartDrill[nIndH].first) continue ; // se il foro ha una lunghezza inferiore al MaxMaterial dell'utensile, allora è lavorato completamente double dAddLen = ( vHoles[nIndH].hole.bBlind ? 0 : m_Params.m_dThroughAddLen) ; if ( ( vHoles[nIndH].hole.dLen + dAddLen) < vTools[nIndT].pTool->m_dMaxMat + EPS_SMALL) { vbPartDrill[nIndH].first = false ; vbPartDrill[nIndH].second = 0. ; } else { vbPartDrill[nIndH].second = min( vbPartDrill[nIndH].second, ( vHoles[nIndH].hole.dLen + dAddLen) - vTools[nIndT].pTool->m_dMaxMat) ; } } } // tutti i fori non lavorati completamente, quindi per i quali sono inseriti SOLO tools di MaxMaterial // inferiore alla lunghezza stessa, sono lavorati parzialmente ( una maschera di compatibilità è // comunque stata trovata ) for ( int i = 0 ; i < int( vbPartDrill.size()) ; ++ i) { if ( vbPartDrill[i].first) vIdPartialdrilledHoles.emplace_back( make_pair( vHoles[i].hole.nOriId, vbPartDrill[i].second)) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::CheckBasedConfig( const VECTORHOLE& vHoles, int nHoleInd, int& nValidConfig, bool& bBaseCase) { // controllo dei parametri if ( nHoleInd < 0 || nHoleInd >= int( vHoles.size())) return false ; if ( vHoles.empty()) return false ; bBaseCase = false ; nValidConfig = 0 ; Vector3d vtAuxRef ; for ( int i = 0 ; i < int( vHoles[nHoleInd].vIndTools.size()) ; ++ i) { if ( vHoles[nHoleInd].vIndTools[i] != IND_TOOL_INVALID) { ++ nValidConfig ; if ( nValidConfig == 1) vtAuxRef = vHoles[nHoleInd].vVtAux[i] ; else { // controllo se i versori ausiliari sono opposti if ( ! AreOppositeVectorApprox( vtAuxRef, vHoles[nHoleInd].vVtAux[i])) return true ; } } } bBaseCase = ( nValidConfig == 1 || nValidConfig == 2) ; return true ; } //---------------------------------------------------------------------------- bool Drilling::MultiHeadOrderConfig( TABMHDRILL& tabDrills, const VECTORHOLE& vHoles, const Vector3d& vtTool, const Vector3d& vtAux) { /* funzione per riordinare le calate; viene creato un frame mediante vtTool e vtAux e si ordinano i punti in locale a tale frame in base alla vicinanza che hanno dal punto minimo del boxXY */ // se non ho calate, non faccio nulla if ( tabDrills.empty()) return true ; // creo il sistema di riferimento locale Frame3d frOrder ; if ( ! frOrder.Set( ORIG, vtTool, vtAux)) return false ; // creo un vettore con i punti sulla base superiore dei fori in locale a tale frame // li inserisco anche nel Box PNTVECTOR vPtLoc ; BBox3d BBoxOrder ; for ( int i = 0 ; i < int( tabDrills.size()) ; ++ i) { vPtLoc.emplace_back( GetToLoc( vHoles[tabDrills[i].nHoleInd].hole.ptIni, frOrder)) ; BBoxOrder.Add( vPtLoc.back()) ; } // recupero il punto minimo del Box Point3d ptMinBoxOrder = BBoxOrder.GetMin() ; // cerco l'indice della tabDrills il cui foro è più vicino al punto di minimo del box trovato int nIndClosestHole = 0 ; double dSqMinDist = INFINITO ; for ( int i = 0 ; i < int( vPtLoc.size()) ; ++ i) { double dCurrSqDist = SqDist( vPtLoc[i], ptMinBoxOrder) ; if ( dCurrSqDist < dSqMinDist) { dSqMinDist = dCurrSqDist ; nIndClosestHole = i ; } } // metto l'indice trovato in prima posizione swap( tabDrills[nIndClosestHole], tabDrills[0]) ; // una volta determinato il primo foro struct OrderTabDrill { int nHoleInd ; Point3d ptIni ; OrderTabDrill( int nHInd, Point3d ptI) : nHoleInd( nHInd), ptIni( ptI) {} ; } ; vector vOrderedTab ; for ( int i = 0 ; i < int( tabDrills.size()) ; ++ i) vOrderedTab.emplace_back( tabDrills[i].nHoleInd, vHoles[tabDrills[i].nHoleInd].hole.ptIni) ; // per sicurezza ... if ( vOrderedTab.empty()) return false ; // prendo come riferimento il primo punto Point3d ptRef = vOrderedTab[0].ptIni ; double dMinSqDist = INFINITO ; int nIndexSwitch = -1 ; // ordino le successioni di calate for ( int i = 0 ; i < int( vOrderedTab.size()) - 1 ; ++ i) { for ( int j = i + 1 ; j < int( vOrderedTab.size()) ; ++ j) { Point3d ptS = vOrderedTab[j].ptIni ; double dSqCurrDist = SqDist( ptS, ptRef) ; if ( dSqCurrDist < dMinSqDist) { dMinSqDist = dSqCurrDist ; nIndexSwitch = j ; } } if ( nIndexSwitch != i + 1) { swap( vOrderedTab[nIndexSwitch], vOrderedTab[i + 1]) ; swap( tabDrills[nIndexSwitch], tabDrills[i + 1]) ; } ptRef = vOrderedTab[i + 1].ptIni ; dMinSqDist = INFINITO ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::GetHoleBestConfig( VECTORHOLE& vHoles, const VECTORTOOL& vTools, int nInd, TABMHDRILL& tabDrills, const Vector3d& vtAux, const Vector3d& vtTool, int& nOkHole) { // salvo gli indici dei fori ( non ancora lavorati) che vengono lavorati con la sola consifugrazione presente int nMaxDrilledHoles = - 1 ; INTVECTOR vInds ; int nRefConfig = -1 ; // scorro le configurazioni presenti for ( int i = 0 ; i < int( vHoles[nInd].vToolHole.size()) ; ++ i) { // se configurazione non valida passo alla successiva if ( vHoles[nInd].vToolHole[i] == IND_TOOL_INVALID || /*&&*/ vHoles[nInd].vIndTools[i] == IND_TOOL_INVALID || /*&&*/ ! vHoles[nInd].vVtAux[i].IsValid()) continue ; // determino se esiste la prima configurazione ammissibile per tale foro INTVECTOR vIndsTemp ; if ( ! CalcDrilledHolesByConfig( vHoles, nInd, i, vIndsTemp)) return false ; if ( int( vIndsTemp.size()) > nMaxDrilledHoles) { nMaxDrilledHoles = int( vIndsTemp.size()) ; nRefConfig = i ; vInds = vIndsTemp ; } else if ( int( vIndsTemp.size()) == nMaxDrilledHoles) { double dAngDeg0, dAngDeg1 ; bool bDet0, bDet1 ; if ( ! vHoles[nInd].vVtAux[nRefConfig].GetRotation( vtAux, vtTool, dAngDeg0, bDet0) || ! vHoles[nInd].vVtAux[i].GetRotation( vtAux, vtTool, dAngDeg1, bDet1)) return false ; if ( abs( dAngDeg0) > abs( dAngDeg1) + EPS_ANG_SMALL) { nRefConfig = i ; vInds = vIndsTemp ; } } } // assegno flag per fori lavorati for ( int i = 0 ; i < int( vInds.size()) ; ++ i) vHoles[vInds[i]].bDrill = true ; // assegno posizioni di ToolMain if ( nRefConfig != -1) { // creazione matrice di Maschera INTINTVECTOR vMyMask ; if ( ! MultiHeadHoleToolsConfig( vHoles, nRefConfig, vMyMask)) return false ; // per forza tutte le uscite sono attive INTVECTOR vIndActiveExist ; for ( int nT = 0 ; nT < int( vTools.size()) ; ++ nT) vIndActiveExist.emplace_back( vTools[nT].pTool->m_nExit) ; tabDrills.emplace_back( vHoles[nInd].vToolHole[nRefConfig], vHoles[nInd].vVtAux[nRefConfig], vIndActiveExist, vTools[vHoles[nInd].vIndMainTool[nRefConfig]], vMyMask) ; nOkHole += int( vInds.size()) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::MultiHeadHoleToolsConfig( const VECTORHOLE& vHoles, int nConfig, INTINTVECTOR& vConfMask) { // controllo dei parametri vConfMask.clear() ; if ( vHoles.empty()) return true ; if ( nConfig < 0 || nConfig >= int( vHoles[0].vIndMainTool.size())) return false ; // scorro i fori for ( int nH = 0 ; nH < int( vHoles.size()) ; ++ nH) { // recupero la configurazione attuale se valida if ( vHoles[nH].vIndTools[nConfig] != IND_TOOL_INVALID && vHoles[nH].vIndMainTool[nConfig] != IND_TOOL_INVALID && vHoles[nH].vVtAux[nConfig].IsValid()) vConfMask.emplace_back( make_pair( nH, vHoles[nH].vIndTools[nConfig])) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::MultiHeadDrilling( const SELVECTOR& vId, int nClId, bool bFixed, TABMHDRILL& tabDrills, double& dMHOff) { // controllo parametri tabDrills.clear() ; if ( vId.empty()) return true ; // gestore degli utensili ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ; if ( pTMgr == nullptr) return false ; // recupero il nome della testa e il numero di uscite string sHead ; m_pMchMgr->GetCurrMachine()->GetCurrHead( sHead) ; int nExitCnt = m_pMchMgr->GetCurrMachine()->GetHeadExitCount( sHead) ; // Recupero i dati degli utensili montati sulla testa int nMainToolInd = -1 ; VECTORTOOL vTools ; vTools.reserve( nExitCnt) ; // ricavo gli utensili presenti sulle uscite for ( int nT = 0 ; nT < nExitCnt ; ++ nT) { string sToolName ; if ( ! m_pMchMgr->GetLoadedTool( sHead, nT + 1, sToolName) || sToolName.empty()) { // non c'è utensile vTools.emplace_back( nullptr) ; continue ; } // se presente e valido const ToolData* pTdata = pTMgr->GetTool( sToolName) ; vTools.emplace_back( pTdata) ; // imposto il tool di riferimento come il tool m_TParams if ( pTdata->m_Uuid == m_TParams.m_Uuid) nMainToolInd = nT ; } // se non ho utensili attivi o validi, errore if ( nMainToolInd == -1 || vTools.empty()) { m_pMchMgr->SetLastError( 2116, "Error in Drilling : multi drilling head without valid tools") ; return false ; } // recupero i dati dell'uscita dell'utensile principale Vector3d vtTool, vtAux ; Point3d ptExit ; m_pMchMgr->GetCurrMachine()->GetHeadExitPosDirAux( sHead, nMainToolInd + 1, ptExit, vtTool, vtAux) ; vTools[nMainToolInd].ptToolTip = ptExit - vtTool * vTools[nMainToolInd].pTool->m_dLen ; if ( vtAux.IsSmall() && m_pMchMgr->GetCurrMachine()->GetCurrRotAxes() == 0) vtAux = FromUprightOrtho( vtTool) ; if ( vtTool.IsSmall() || vtAux.IsSmall()) { m_pMchMgr->SetLastError( 2117, "Error in Drilling : incorrect multi drilling head") ; return false ; } // carico gli altri dati della testa con le sue uscite for ( int nT = 0 ; nT < nExitCnt ; ++ nT) { // se utensile principale, salto al successivo if ( nT == nMainToolInd) continue ; // se non attrezzato, salto al successivo if ( vTools[nT].pTool == nullptr) continue ; // recupero i dati dell'uscita Point3d ptExit ; Vector3d vtCurrDir, vtCurrAux ; m_pMchMgr->GetCurrMachine()->GetHeadExitPosDirAux( sHead, nT + 1, ptExit, vtCurrDir, vtCurrAux) ; // controllo abbia la stessa direzione del principale, altrimenti disattrezzo if ( ! AreSameVectorApprox( vtCurrDir, vtTool)) { vTools[nT].pTool = nullptr ; continue ; } // assegno tip utensile vTools[nT].ptToolTip = ptExit - vtCurrDir * vTools[nT].pTool->m_dLen ; } // Recupero le geometrie dei fori bool bSomeHoleOk = false ; VECTORHOLE vHoles( vId.size()) ; for ( int nH = 0 ; nH < int( vId.size()) ; ++ nH) { Hole hole ; if ( ! GetHoleData( m_vId[nH], hole)) { string sInfo = "Warning in Drilling : Skipped entity " + ToString( m_vId[nH]) ; m_pMchMgr->SetWarning( 2151, sInfo) ; m_vUndrilledId.emplace_back( m_vId[nH].nId) ; continue ; } // se richiesta inversione e foro passante, provvedo if ( m_Params.m_bInvert) { if ( hole.bBlind) { m_pMchMgr->SetLastError( 2114, "Error in Drilling : blind hole not reversible") ; return false ; } else { hole.ptIni -= hole.vtDir * hole.dThick ; hole.vtDir.Invert() ; } } // se lavorazione del foro non arriva al suo fondo, lo considero cieco if ( hole.dLen < hole.dThick - 10 * EPS_SMALL) hole.bBlind = true ; vHoles[nH].hole = hole ; vHoles[nH].nIndInSelVector = nH ; bSomeHoleOk = true ; } if ( ! bSomeHoleOk) return true ; // imposto eventuale asse bloccato da lavorazione SetBlockedRotAxis( m_Params.m_sBlockedAxis) ; // applico eventuali blocchi di assi rotanti m_pMchMgr->ApplyRotAxisBlock() ; // se utensili non selezionabili // NB. Queste due funzioni possono essere accorpate in una unica... dipende da quanto voglio essere // selettivo nei criteri di scelta della maschera if ( bFixed) { // calcolo la maschera di corrispondenza tra utensili e fori if ( ! CalcMask( vHoles, vTools, nMainToolInd, vtTool, vtAux)) return false ; } // se utensili selezionabili else { // calcolo la maschera di corrispondenza tra utensili e fori if ( ! CalcMaskSel( vHoles, vTools, vtTool, vtAux)) return false ; } #if ENABLE_DEBUG_MULTIHEAD_HOLES PrintConfigs( vHoles) ; #endif // riduco il numero di configurazioni in base a vtAux if ( ! KeepMinRotatedConfigs( vHoles, vtAux, vtTool)) return false ; // calcolo i fori non lavorati if ( ! CalcMultiHeadUndrilledHoles( vHoles, m_vUndrilledId)) return false ; // se non posso lavorare nessun foro, esco int nUndrilleds = int( m_vUndrilledId.size()) ; if ( nUndrilleds == int( vHoles.size())) return true ; // numero di fori lavorati int nOkHole = 0 ; // se c'è un solo foro va sicuramente bene if ( int( vHoles.size()) == 1 && ! vHoles[0].vVtAux.empty()) { vHoles[0].bForToolM = true ; vHoles[0].nIndTool = nMainToolInd ; vHoles[0].nIndHoleToolM = 0 ; vHoles[0].vtAux = vHoles[0].vVtAux[0] ; } // calcolo la massima differenza di lunghezza tra il primo tool e gli altri ( così l'elevazione finale rimane compatibile) double dRefLen = vTools[nMainToolInd].pTool->m_dTLen ; double dOffMax = 0 ; for ( int nT = 0 ; nT < int( vTools.size()) && nUndrilleds != int( vHoles.size()) ; ++ nT) { if ( vTools[nT].pTool == nullptr || nT == nMainToolInd) continue ; if ( vTools[nT].pTool->m_dTLen > dRefLen + dOffMax) dOffMax = vTools[nT].pTool->m_dTLen - dRefLen ; } dMHOff = dOffMax ; // se testa fissa if ( bFixed) { // Inizio a considerare i fori che presentano meno configurazioni per essere lavorati. // Essi sono i più critici, quindi vengono gestiti per primi, scremando quindi le configurazioni degli altri. // Parto ad analizzare i fori lavorabili con una sola possibile configurazione // cerco le configurazioni di base ( quelle con 1 o due configurazioni opposte) for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { // se foro già lavorato passo al successivo if ( vHoles[i].bDrill) continue ; // se il foro i-esimo non è base, passo al successivo int nValidConfig ; bool bBaseCase ; if ( ! CheckBasedConfig( vHoles, i, nValidConfig, bBaseCase)) return false ; if ( ! bBaseCase) continue ; // salvo gli indici dei fori ( non ancora lavorati) che vengono lavorati con la sola configurazione presente if ( ! GetHoleBestConfig( vHoles, vTools, i, tabDrills, vtAux, vtTool, nOkHole)) return false ; } // se nessun foro lavorabile, esco ( fino a prova contraria non esco mai...) if ( tabDrills.empty()) return false ; // Una volta scremate le configurazioni critiche, cerco i fori non lavorati più vicini ad essi. // L'idea è che avendo lavorato i fori a signola configurazione, ottengo meno combinazioni // per i fori ad esso adiacenti... l'idea si ripete per tutti i fori successivi // per vicinanza cerco gli altri fori oltre ai casi base while ( nOkHole < int( vHoles.size()) - nUndrilleds) { INTVECTOR vNextInds ; if ( ! GetClosestHolesToHole( vHoles, tabDrills.back().nHoleInd, false, vNextInds) || vNextInds.empty() || ! GetHoleBestConfig( vHoles, vTools, vNextInds[0], tabDrills, vtAux, vtTool, nOkHole)) return false ; } // per sicurezza ... if ( tabDrills.empty()) return false ; // Analizzando i fori in base alle possibili combinazioni, ho riempito la tabella delle forature // multiple con lavorazioni sparse tra loro, riordino quindi le discese degli utensili in base // alla vicinanza dei fori lavorati dal Tool principale if ( ! MultiHeadOrderConfig( tabDrills, vHoles, vtTool, vtAux)) return false ; } // se utensili selezionabili else { // Inizio a considerare le configurazioni che presentano più utensili attivi, decrementando // con il numero di passsate gli utensili selezionati if ( ! OrderConfigsForSelectableTools( vHoles, vTools, nMainToolInd, tabDrills)) return false ; } // controllo i fori lavorati parzialmente dalle configurazioni trovate if ( ! CalcMultiHeadPartialDrilledHoles( tabDrills, vHoles, vTools, m_vUndrilledId, m_vPartialDrilledId)) return false ; #if ENABLE_DEBUG_MULTIHEAD_HOLES string sRow = "Undrilled Holes : " ; for ( int _a = 0 ; _a < int( m_vUndrilledId.size()) ; ++ _a) sRow.append( ( _a == 0 ? "" : ",") + ToString( m_vUndrilledId[_a])) ; LOG_ERROR( GetEMkLogger(), sRow.c_str()) ; string sRow1 = "Partial Drilled Holes : " ; for ( int _a = 0 ; _a < int( m_vPartialDrilledId.size()) ; ++ _a) { sRow1.append( ( _a == 0 ? "" : ",") + ToString( m_vPartialDrilledId[_a].first)) ; sRow1.append( " Missing Len : " + ToString( m_vPartialDrilledId[_a].second)) ; } LOG_ERROR( GetEMkLogger(), sRow1.c_str()) ; #endif return true ; } //---------------------------------------------------------------------------- bool Drilling::CalcMask( VECTORHOLE& vHoles, const VECTORTOOL& vTools, int nIndMT, const Vector3d& vtTool, const Vector3d& vtAux) { /* funzione per il calcolo della maschera di compatibilità tra un insieme di utensili e un insieme di fori. Funzione chiamata per utensili non selezionabili */ // controllo dei parametri if ( vHoles.empty() || vTools.empty()) return false ; // recupero il valore di tolleranza sul diametro double dDiamToler = GetHoleDiamToler() ; int nExitCnt = int( vTools.size()) ; int nNullTools = 0 ; for ( int i = 0 ; i < int( vTools.size()) ; ++ i) { if ( vTools[i].pTool == nullptr) ++ nNullTools ; } // in ogni foro i-esimo inserisco il Tool principale for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { // resetto le variabili di controllo di lavorazione temporanea per tutti i fori for ( int j = 0 ; j < int( vHoles.size()) ; ++ j) { vHoles[j].bTempDrill = false ; vHoles[j].nTempTool = -1 ; } // verifica validità del foro i-esimo per il Tool principale Hole Hole_i = vHoles[i].hole ; if ( ! MultiHeadVerifyHole( Hole_i, vTools[nIndMT].pTool, dDiamToler, m_vId[vHoles[i].nIndInSelVector])) continue ; // angoli per allineare versore T del tool principale con vtDir del foro i-esimo int nStat ; DBLVECTOR vAng1, vAng2 ; if ( ! m_pMchMgr->GetCalcAngles( Hole_i.vtDir, V_NULL, nStat, vAng1, vAng2)) continue ; Vector3d vtTnew ; // check che versore T sia allineato con vtDir if ( ! m_pMchMgr->GetCalcToolDirFromAngles( vAng1, vtTnew) || ! AreSameVectorApprox( vtTnew, Hole_i.vtDir)) continue ; Vector3d vtAnew ; // nuova configurazione del versore A ottenuta if ( ! m_pMchMgr->GetCalcAuxDirFromAngles( vAng1, vtAnew)) continue ; if ( vtAnew.IsSmall() && m_pMchMgr->GetCurrMachine()->GetCurrRotAxes() == 0) vtAnew = vtAux ; // creo un nuovo sistema di riferimento centrato nel Tool principale Frame3d frMT ; frMT.Set( vTools[nIndMT].ptToolTip, vtTool, vtAux) ; if ( ! frMT.IsValid()) return false ; // creo un frame nel foro i-esimo orientato come il frame sul tool principale ( origine nella base interna del foro) Frame3d frHMT ; frHMT.Set( Hole_i.ptIni - Hole_i.vtDir * Hole_i.dLen, vtTnew, vtAnew) ; if ( ! frHMT.IsValid()) return false ; // numero di forature inserendo il Tool principale nel foro i-esimo int nDrills = 1 ; // setto le variabili temporanee per il foro i-esimo vHoles[i].bTempDrill = true ; vHoles[i].nTempTool = nIndMT ; // controllo la compatibilità tra le geometrie dei Tool e dei fori CheckOtherHolesWithTools( vHoles, vTools, nIndMT, i, Hole_i, frMT, frHMT, dDiamToler, nDrills) ; // controllo quanti fori sono riuscito a lavorare e setto i loro parametri di foratura if ( nDrills == nExitCnt - nNullTools) { // se ho lavorato il numero corretto di fori ... for ( int nD = 0 ; nD < int( vHoles.size()) ; ++ nD) { if ( vHoles[nD].bTempDrill) { vHoles[nD].vToolHole.push_back( i) ; // indice del foro per il tool principale vHoles[nD].vIndTools.push_back( vHoles[nD].nTempTool) ; // indice del tool che lavora questo foro vHoles[nD].vVtAux.push_back( frHMT.VersX()) ; // versore ausiliario A del tool principale vHoles[nD].vIndMainTool.push_back( nIndMT) ; // indice del tool considerato come principale } else { // rendo tutto non valido vHoles[nD].vToolHole.push_back( IND_TOOL_INVALID) ; vHoles[nD].vIndTools.push_back( IND_TOOL_INVALID) ; vHoles[nD].vVtAux.push_back( V_INVALID) ; vHoles[nD].vIndMainTool.push_back( nIndMT) ; } } } else { for ( int nD = 0 ; nD < int( vHoles.size()) ; ++ nD) vHoles[nD].bTempDrill = false ; } // se la testa può ruotare if ( nStat < 0) { // inizio a scorrere tutti i tools for ( int nT = 0 ; nT < int( vTools.size()) ; ++ nT) { if ( vTools[nT].pTool == nullptr || nT == nIndMT) continue ; // cerco se ho un foro j-esimo adatto per quella punta for ( int j = 0 ; j < int( vHoles.size()) ; ++ j) { Hole Hole_j = vHoles[j].hole ; if ( i == j || ! AreSameVectorApprox( Hole_j.vtDir, Hole_i.vtDir) || ! MultiHeadVerifyHole( Hole_j, vTools[nT].pTool, dDiamToler, m_vId[vHoles[j].nIndInSelVector])) continue ; // resetto le variabili di controllo di foratura temporanea per tutti i fori for ( int k = 0 ; k < int( vHoles.size()) ; ++ k) { vHoles[k].bTempDrill = false ; vHoles[k].nTempTool = IND_TOOL_INVALID ; } // calcolo il vettore uscente dal tool Main e diretto al tool nT-esimo Vector3d vtRefT = vTools[nT].ptToolTip - vTools[nIndMT].ptToolTip ; // calcolo il vettore uscente dal foro i-esimo e diretto al foro j-esimo Vector3d vtRefH = Hole_j.ptIni - Hole_j.vtDir * Hole_j.dLen - ( Hole_i.ptIni - Hole_i.vtDir * Hole_i.dLen) ; // se le distanze sono compatibili if ( abs( vtRefH.Len() - vtRefT.Len()) < EPS_SMALL) { // rioriento il frame sul foro i-esimo Point3d ptHj = Hole_j.ptIni - Hole_j.vtDir * Hole_j.dLen ; ptHj.ToLoc( frHMT) ; Vector3d vtProjB( ptHj.x, ptHj.y, 0) ; Point3d ptTt = vTools[nT].ptToolTip ; ptTt.ToLoc( frMT) ; Vector3d vtProjA( ptTt.x, ptTt.y, 0) ; double dAngle ; vtProjA.GetAngleXY( vtProjB, dAngle) ; frHMT.Rotate( frHMT.Orig(), frHMT.VersZ(), dAngle) ; nDrills = 1 ; // foratura nel foro i-esimo e j-esimo CheckOtherHolesWithTools( vHoles, vTools, nIndMT, i, Hole_i, frMT, frHMT, dDiamToler, nDrills) ; // setto le variabili temporanee per il foro i-esimo vHoles[i].bTempDrill = true ; vHoles[i].nTempTool = nIndMT ; // controllo quanti fori sono riuscito a lavorare in questo nuovo sistema di riferimento if ( nDrills == nExitCnt - nNullTools) { for ( int nD = 0 ; nD < int( vHoles.size()) ; ++ nD) { if ( vHoles[nD].bTempDrill) { vHoles[nD].vToolHole.push_back( i) ; // indice del foro per il tool principale vHoles[nD].vIndTools.push_back( vHoles[nD].nTempTool) ; // indice del tool che lavora questo foro vHoles[nD].vVtAux.push_back( frHMT.VersX()) ; // versore ausiliario del tool principale vHoles[nD].vIndMainTool.push_back( nIndMT) ; } else { // rendo tutto non valido vHoles[nD].vToolHole.push_back( IND_TOOL_INVALID) ; vHoles[nD].vIndTools.push_back( IND_TOOL_INVALID) ; vHoles[nD].vVtAux.push_back( V_INVALID) ; vHoles[nD].vIndMainTool.push_back( nIndMT) ; } } } else { for ( int nD = 0 ; nD < int( vHoles.size()) ; ++ nD) vHoles[nD].bTempDrill = false ; } } } } } } // cancello le configurazioni identiche tra loro ( possono capitare quando i tools sono già // allineati con i fori) EraseDuplicatedConfigs( vHoles) ; return true ; } //---------------------------------------------------------------------------- bool Drilling::CalcMaskSel( VECTORHOLE& vHoles, const VECTORTOOL& vTools, const Vector3d& vtTool, const Vector3d& vtAux) { /* funzione per il calcolo della maschera di compatibilità tra un insieme di utensili e un insieme di fori. Funzione chiamata per utensili non selezionabili */ // controllo dei parametri if ( vHoles.empty() || vTools.empty()) return false ; // recupero il valore di tolleranza sul diametro double dDiamToler = GetHoleDiamToler() ; // per ogni foro i-esimo inserisco un utensile selezionabile for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { // resetto le variabili di controllo di lavorazione temporanea per tutti i fori for ( int k = 0 ; k < int( vHoles.size()) ; ++ k) { vHoles[k].bTempDrill = false ; vHoles[k].nTempTool = -1 ; } // ciclo sugli utensili selezionabili for ( int j = 0 ; j < int( vTools.size()) ; ++ j) { // se utensile non valido, passo al successivo if ( vTools[j].pTool == nullptr) continue ; for ( int k = 0 ; k < int( vHoles.size()) ; ++ k) { vHoles[k].bTempDrill = false ; vHoles[k].nTempTool = -1 ; } // verifica validità del foro i-esimo per il Tool principale Hole Hole_i = vHoles[i].hole ; if ( ! MultiHeadVerifyHole( Hole_i, vTools[j].pTool, dDiamToler, m_vId[vHoles[i].nIndInSelVector])) continue ; // angoli per allineare versore T del tool principale con vtDir del foro i-esimo int nStat ; DBLVECTOR vAng1, vAng2 ; if ( ! m_pMchMgr->GetCalcAngles( Hole_i.vtDir, V_NULL, nStat, vAng1, vAng2)) continue ; Vector3d vtTnew ; // check che versore T sia allineato con vtDir if ( ! m_pMchMgr->GetCalcToolDirFromAngles( vAng1, vtTnew) || ! AreSameVectorApprox( vtTnew, Hole_i.vtDir)) continue ; Vector3d vtAnew ; // nuova configurazione del versore A ottenuta if ( ! m_pMchMgr->GetCalcAuxDirFromAngles( vAng1, vtAnew)) continue ; if ( vtAnew.IsSmall() && m_pMchMgr->GetCurrMachine()->GetCurrRotAxes() == 0) vtAnew = vtAux ; // creo un nuovo sistema di riferimento centrato nel Tool j-esimo Frame3d frMT ; frMT.Set( vTools[j].ptToolTip, vtTool, vtAux) ; if ( ! frMT.IsValid()) return false ; // creo un frame nel foro i-esimo orientato come il frame sul tool principale ( origine nella base interna del foro) Frame3d frHMT ; frHMT.Set( Hole_i.ptIni - Hole_i.vtDir * Hole_i.dLen, vtTnew, vtAnew) ; if ( ! frHMT.IsValid()) return false ; // numero di forature inserendo il Tool j-esimo nel foro i-esimo int nDrills = 1 ; // setto le variabili temporanee per il foro i-esimo vHoles[i].bTempDrill = true ; vHoles[i].nTempTool = j ; // controllo la compatibilità tra le geometrie dei Tool e dei fori CheckOtherHolesWithTools( vHoles, vTools, j, i, Hole_i, frMT, frHMT, dDiamToler, nDrills) ; // memorizzo la configurazione attuale for ( int nD = 0 ; nD < int( vHoles.size()) ; ++ nD) { // se il foro è lavorabile, memorizzo la configurazione if ( vHoles[nD].bTempDrill) { vHoles[nD].vToolHole.push_back( i) ; // indice del foro per il tool principale vHoles[nD].vIndTools.push_back( vHoles[nD].nTempTool) ; // indice del tool che lavora questo foro vHoles[nD].vVtAux.push_back( frHMT.VersX()) ; // versore ausiliario A del tool principale vHoles[nD].vIndMainTool.push_back( j) ; // indice del tool considerato come principale } else { // rendo tutto non valido vHoles[nD].vToolHole.push_back( IND_TOOL_INVALID) ; vHoles[nD].vIndTools.push_back( IND_TOOL_INVALID) ; vHoles[nD].vVtAux.push_back( V_INVALID) ; vHoles[nD].vIndMainTool.push_back( j) ; } } // se la testa può ruotare if ( nStat < 0) { // resetto le variabili di controllo di lavorazione temporanea per tutti i fori for ( int k = 0 ; k < int( vHoles.size()) ; ++ k) { vHoles[k].bTempDrill = false ; vHoles[k].nTempTool = -1 ; } // inizio a scorrere tutti i tools ( ad eccezione del tool j-esimo) for ( int nT = 0 ; nT < int( vTools.size()) ; ++ nT) { if ( vTools[nT].pTool == nullptr || nT == j) continue ; // cerco se ho un foro nH-esimo adatto per quella punta for ( int nH = 0 ; nH < int( vHoles.size()) ; ++ nH) { Hole Hole_nH = vHoles[nH].hole ; if ( i == nH || ! AreSameVectorApprox( Hole_nH.vtDir, Hole_i.vtDir) || ! MultiHeadVerifyHole( Hole_nH, vTools[nT].pTool, dDiamToler, m_vId[vHoles[nH].nIndInSelVector])) continue ; // resetto le variabili di controllo di foratura temporanea per tutti i fori for ( int k = 0 ; k < int( vHoles.size()) ; ++ k) { vHoles[k].bTempDrill = false ; vHoles[k].nTempTool = IND_TOOL_INVALID ; } // calcolo il vettore uscente dal tool j-esimo e diretto al tool nT-esimo Vector3d vtRefT = vTools[nT].ptToolTip - vTools[j].ptToolTip ; // calcolo il vettore uscente dal foro i-esimo e diretto al foro nH-esimo Vector3d vtRefH = Hole_nH.ptIni - Hole_nH.vtDir * Hole_nH.dLen - ( Hole_i.ptIni - Hole_i.vtDir * Hole_i.dLen) ; // se le distanze sono compatibili if ( abs( vtRefH.Len() - vtRefT.Len()) < EPS_SMALL) { // rioriento il frame sul foro i-esimo Point3d ptHj = Hole_nH.ptIni - Hole_nH.vtDir * Hole_nH.dLen ; ptHj.ToLoc( frHMT) ; Vector3d vtProjB( ptHj.x, ptHj.y, 0) ; Point3d ptTt = vTools[nT].ptToolTip ; ptTt.ToLoc( frMT) ; Vector3d vtProjA( ptTt.x, ptTt.y, 0) ; double dAngle ; vtProjA.GetAngleXY( vtProjB, dAngle) ; frHMT.Rotate( frHMT.Orig(), frHMT.VersZ(), dAngle) ; nDrills = 1 ; // foratura nel foro i-esimo e jj-esimo CheckOtherHolesWithTools( vHoles, vTools, j, i, Hole_i, frMT, frHMT, dDiamToler, nDrills) ; // setto le variabili temporanee per il foro i-esimo vHoles[i].bTempDrill = true ; vHoles[i].nTempTool = j ; // memorizzo la configurazione attuale for ( int nD = 0 ; nD < int( vHoles.size()) ; ++ nD) { if ( vHoles[nD].bTempDrill) { vHoles[nD].vToolHole.push_back( i) ; // indice del foro per il tool principale vHoles[nD].vIndTools.push_back( vHoles[nD].nTempTool) ; // indice del tool che lavora questo foro vHoles[nD].vVtAux.push_back( frHMT.VersX()) ; // versore ausiliario del tool principale vHoles[nD].vIndMainTool.push_back( j) ; // indice del tool considerato come principale } else { // rendo tutto non valido vHoles[nD].vToolHole.push_back( IND_TOOL_INVALID) ; vHoles[nD].vIndTools.push_back( IND_TOOL_INVALID) ; vHoles[nD].vVtAux.push_back( V_INVALID) ; vHoles[nD].vIndMainTool.push_back( j) ; } } } } } } } } // cancello le configurazioni identiche tra loro ( possono capitare quando i tools sono già // allineati con i fori) EraseDuplicatedConfigs( vHoles) ; return true ; } //---------------------------------------------------------------------------- bool Drilling::CheckOtherHolesWithTools( VECTORHOLE& vHoles, const VECTORTOOL& vTools, int nIndTM, int nIndHTM, Hole holeICP, const Frame3d& frMT, const Frame3d& frHMT, double dDiamToler, int& nDrills) { /* supponendo di inserire il tool principale nel foro nIndTM-esimo vengono controllate le compatibilità tra gli altri utensili e gli altri fori */ // controllo parametri if ( vHoles.empty() || vTools.empty()) return true ; if ( nIndTM < 0 || nIndTM >= int( vTools.size()) || nIndHTM < 0 || nIndHTM >= int( vHoles.size()) || ! frMT.IsValid() || ! frHMT.IsValid() || nDrills < 0 || nDrills > max( int( vHoles.size()), int( vTools.size()))) return false ; // definisco il punto dove cade il tool principale Hole holeTM = holeICP ; // copia del foro i-esimo // ciclo su tutti i tools for ( int nT = 0 ; nT < int( vTools.size()) ; ++ nT) { if ( vTools[nT].pTool == nullptr || nT == nIndTM) continue ; // esprimo il punto finale del tool t-esimo nel sistema di riferimento del Tool principale Point3d ptETt = vTools[nT].ptToolTip ; ptETt.ToLoc( frMT) ; // cerco se ho un foro j-esimo adatto per quella punta for ( int j = 0 ; j < int( vHoles.size()) ; ++ j) { Hole Hole_j = vHoles[j].hole ; // copia del foro j-esimo // controllo che il foro j-esimo non sia l'i-esimo, che non sia stato già precedentemente lavorato // da un altro tool t'-esimo e che vtDir del foro i-esimo coincida con vtDir del foro j-esimo // e che il tool t-esimo sia compatibile con il foro j-esimo if ( nIndHTM == j || vHoles[j].bTempDrill || ! AreSameVectorApprox( Hole_j.vtDir, holeTM.vtDir) || ! MultiHeadVerifyHole( Hole_j, vTools[nT].pTool, dDiamToler, m_vId[vHoles[j].nIndInSelVector])) continue ; // esprimo il foro j-esimo nel sistema di riferimento del Tool principale centrato nel foro i-esimo Point3d ptHj = Hole_j.ptIni - Hole_j.vtDir * Hole_j.dLen ; ptHj.ToLoc( frHMT) ; // controllo la compatibilità if ( AreSamePointApprox( ptHj, ptETt)) { // aggiorno il numero di fori lavorati ++ nDrills ; // aggiorno le variabili temporanee vHoles[j].bTempDrill = true ; vHoles[j].nTempTool = nT ; // non controllo gli altri vertici per la punta t-esima break ; } } } return true ; } //---------------------------------------------------------------------------- bool Drilling::MultiHeadVerifyHole( Hole& hole, const ToolData* Tool, double dDiamToler, SelData Id) { /* funzione per verificare la compatibilità tra un utensile e un foro, vengono controllati i diametri, le profondità e i percorsi dal basso */ // verifico che il diamtro del tool sia compatibile con quello del foro if ( ! VerifyDiameter( hole.dDiam, Tool->m_dDiam, dDiamToler)) return false ; // imposto elevazione da lunghezza foro con possibilità di sovrascrittura da info double dElev = hole.dLen ; double dMaxElev ; if ( GetValInNotes( m_Params.m_sUserNotes, UN_MAXELEV, dMaxElev) && dElev > dMaxElev) { dElev = dMaxElev ; hole.ptIni += hole.vtDir * ( dElev - hole.dLen) ; hole.dLen = dElev ; } if ( ! VerifyHoleFromBottom( hole, Id)) return false ; return true ; } //---------------------------------------------------------------------------- bool Drilling::AlongCurveProcess( bool bRecalc, int nPvId, int nClId) { // recupero gruppo per geometria ausiliaria int nAuxId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_AUX) ; bool bChain = false ; // se non c'è, lo aggiungo if ( nAuxId == GDB_ID_NULL) { nAuxId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nAuxId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nAuxId, MCH_AUX) ; m_pGeomDB->SetStatus( nAuxId, GDB_ST_OFF) ; bChain = true ; } // altrimenti, se chiesto ricalcolo, lo lavoro else if ( bRecalc) { m_pGeomDB->EmptyGroup( nAuxId) ; bChain = true ; } // se necessario, eseguo concatenamento ed inserisco i percorsi sotto la geometria ausiliaria if ( bChain && ! Chain( nAuxId)) { m_pMchMgr->SetLastError( 2108, "Error in Drilling : Chaining failed") ; return false ; } // calcolo ogni singola catena int nPathId = m_pGeomDB->GetFirstGroupInGroup( nAuxId) ; while ( nPathId != GDB_ID_NULL) { if ( ! ProcessPath( nPathId, nPvId, nClId)) return false ; nPathId = m_pGeomDB->GetNextGroup( nPathId) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::Chain( int nGrpDestId) { // vettore puntatori alle curve ICURVEPOVECTOR vpCrvs ; vpCrvs.reserve( m_vId.size()) ; // recupero tutte le curve e le porto in globale for ( const auto& Id : m_vId) { // prendo curva vpCrvs.emplace_back( GetCurve( Id)) ; // ne verifico la validità if ( IsNull( vpCrvs.back())) { string sInfo = "Warning in Drilling : Skipped entity " + ToString( Id) ; m_pMchMgr->SetWarning( 2151, sInfo) ; vpCrvs.back().Reset() ; } } // preparo i dati per il concatenamento bool bFirst = true ; Point3d ptNear = ORIG ; double dToler = 10 * EPS_SMALL ; ChainCurves chainC ; chainC.Init( true, dToler, int( vpCrvs.size())) ; for ( size_t i = 0 ; i < vpCrvs.size() ; ++ i) { // recupero la curva e il suo riferimento ICurve* pCrv = vpCrvs[i] ; if ( pCrv == nullptr) continue ; // recupero i dati della curva necessari al concatenamento e li assegno Point3d ptStart, ptEnd ; Vector3d vtStart, vtEnd ; if ( ! pCrv->GetStartPoint( ptStart) || ! pCrv->GetStartDir( vtStart) || ! pCrv->GetEndPoint( ptEnd) || ! pCrv->GetEndDir( vtEnd)) return false ; if ( ! chainC.AddCurve( int( i + 1), ptStart, vtStart, ptEnd, vtEnd)) return false ; // se prima curva, assegno inizio della ricerca if ( bFirst) { ptNear = ptStart + 10 * EPS_SMALL * vtStart ; bFirst = false ; } } // recupero i percorsi concatenati int nCount = 0 ; INTVECTOR vnId2 ; while ( chainC.GetChainFromNear( ptNear, false, vnId2)) { // creo una curva composita PtrOwner pCrvCompo( CreateCurveComposite()) ; if ( IsNull( pCrvCompo)) return false ; // estrusione e spessore Vector3d vtExtr = Z_AX ; double dThick = 0 ; // vettore Id originali SELVECTOR vId2 ; vId2.reserve( vnId2.size()) ; // recupero le curve semplici e le inserisco nella curva composita for ( size_t i = 0 ; i < vnId2.size() ; ++ i) { int nId = abs( vnId2[i]) - 1 ; bool bInvert = ( vnId2[i] < 0) ; vId2.emplace_back( m_vId[nId]) ; // recupero la curva ICurve* pCrv = vpCrvs[nId] ; // se necessario, la inverto if ( bInvert) pCrv->Invert() ; // recupero eventuali estrusione e spessore Vector3d vtTemp ; if ( pCrv->GetExtrusion( vtTemp)) { vtExtr = vtTemp ; double dTemp ; if ( pCrv->GetThickness( dTemp) && abs( dTemp) > abs( dThick)) dThick = dTemp ; } // la aggiungo alla curva composta if ( ! pCrvCompo->AddCurve( ::Release( vpCrvs[nId]), true, dToler)) return false ; } // se non sono state inserite curve, vado oltre if ( pCrvCompo->GetCurveCount() == 0) continue ; // imposto estrusione e spessore pCrvCompo->SetExtrusion( vtExtr) ; pCrvCompo->SetThickness( dThick) ; // aggiorno il nuovo punto vicino pCrvCompo->GetEndPoint( ptNear) ; // creo nuovo gruppo int nPathId = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrpDestId, Frame3d()) ; if ( nPathId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPathId, MCH_PATH + ToString( ++ nCount)) ; m_pGeomDB->SetInfo( nPathId, KEY_IDS, ToString( vId2)) ; // inserisco la curva composita nel gruppo destinazione int nNewId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, ::Release( pCrvCompo)) ; if ( nNewId == GDB_ID_NULL) return false ; } return true ; } //---------------------------------------------------------------------------- ICurve* Drilling::GetCurve( SelData Id) { // accetto solo curve PtrOwner pCurve ; // se direttamente curva if ( Id.nSub == SEL_SUB_ALL) { // recupero e duplico la curva const ICurve* pOriCurve = ::GetCurve( m_pGeomDB->GetGeoObj( Id.nId)) ; if ( pOriCurve != nullptr) pCurve.Set( pOriCurve->Clone()) ; } // altrimenti sottocurva di composita else { // recupero la composita const ICurveComposite* pCompo = GetCurveComposite( m_pGeomDB->GetGeoObj( Id.nId)) ; if ( pCompo != nullptr) { // duplico la curva semplice const ICurve* pOriCurve = ::GetCurve( pCompo->GetCurve( Id.nSub)) ; if ( pOriCurve != nullptr) { pCurve.Set( pOriCurve->Clone()) ; // recupero estrusione e spessore Vector3d vtExtr ; if ( pCompo->GetExtrusion( vtExtr)) pCurve->SetExtrusion( vtExtr) ; double dThick ; if ( pCompo->GetThickness( dThick)) pCurve->SetThickness( dThick) ; } } } if ( IsNull( pCurve)) return nullptr ; // ne recupero il riferimento globale Frame3d frGlob ; if ( ! m_pGeomDB->GetGlobFrame( Id.nId, frGlob)) return nullptr ; // la porto in globale pCurve->ToGlob( frGlob) ; // la restituisco return Release( pCurve) ; } //---------------------------------------------------------------------------- bool Drilling::ProcessPath( int nPathId, int nPvId, int nClId) { // recupero gruppo per geometria temporanea const string GRP_TEMP = "Temp" ; int nTempId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, GRP_TEMP) ; // se non c'è, lo aggiungo if ( nTempId == GDB_ID_NULL) { nTempId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nTempId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nTempId, GRP_TEMP) ; } // altrimenti lo lavoro else m_pGeomDB->EmptyGroup( nTempId) ; // in ogni caso lo dichiaro temporaneo e non visibile m_pGeomDB->SetLevel( nTempId, GDB_LV_TEMP) ; m_pGeomDB->SetStatus( nTempId, GDB_ST_OFF) ; // recupero la curva composita int nCrvId = m_pGeomDB->GetFirstInGroup( nPathId) ; // recupero nome del path string sPName ; m_pGeomDB->GetName( nPathId, sPName) ; // valuto l'espressione dell'offset e della sovrapposizione ExeLuaSetGlobNumVar( "D", m_TParams.m_dDiam) ; double dOffset ; if ( ! ExeLuaEvalNumExpr( m_Params.m_sOffset, &dOffset)) { m_pMchMgr->SetLastError( 2102, "Error in Drilling : Offset not computable") ; return false ; } double dOverlap ; if ( ! ExeLuaEvalNumExpr( m_Params.m_sOverlap, &dOverlap)) { m_pMchMgr->SetLastError( 2103, "Error in Drilling : Overlap not computable") ; return false ; } // disabilito eventuale registrazione comandi EXE (riabilitazione automatica) CmdLogOff cmdLogOff ; // genero le circonferenze int nCount = 0 ; int nFirstId = ExeCreateCirclesAlongCurve( nTempId, nCrvId, dOffset, dOverlap, m_Params.m_dStartAddLen, m_Params.m_dEndAddLen, m_TParams.m_dDiam, &nCount) ; // se richiesta anteprima if ( nPvId != GDB_ID_NULL) { // creo l'anteprima dei fori for ( int i = 0 ; i < nCount ; ++ i) { // identificativo circonferenza int nId = nFirstId + i ; // calcolo il preview del foro if ( ! GenerateHolePv( i, nId, sPName + "_", nPvId)) return false ; } // creo la regione di ingombro dei fori GenerateHoleRegionPv( nFirstId, nCount, nPvId) ; } // se richiesta lavorazione if ( nClId != GDB_ID_NULL) { // creo lavorazione dei fori for ( int i = 0 ; i < nCount ; ++ i) { // identificativo circonferenza int nId = nFirstId + i ; // calcolo la lavorazione del foro if ( ! GenerateHoleCl( i, nId, sPName + "_", nClId)) return false ; } } return true ; } //---------------------------------------------------------------------------- bool Drilling::GenerateHolePv( int nInd, const SelData& nCircId, const string& sPName, int nPvId) { // creo gruppo per geometria di lavorazione del foro int nPathId = m_pGeomDB->AddGroup( GDB_ID_NULL, nPvId, Frame3d()) ; if ( nPathId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPathId, sPName + ToString( nInd + 1)) ; m_pGeomDB->SetInfo( nPathId, KEY_IDS, ToString( nCircId)) ; m_pGeomDB->SetMaterial( nPathId, GREEN) ; // recupero il valore di tolleranza sul diametro double dDiamTol = GetHoleDiamToler() ; // recupero i dati del foro Hole hole ; if ( ! GetHoleData( nCircId, hole) || ! VerifyDiameter( hole.dDiam, m_TParams.m_dDiam, dDiamTol)) { m_pGeomDB->Erase( nPathId) ; string sInfo = "Warning in Drilling : Skipped entity " + ToString( nCircId) ; m_pMchMgr->SetWarning( 2151, sInfo) ; return true ; } // limito lunghezza foro a massima lavorazione della punta if ( hole.dLen > m_TParams.m_dMaxMat + EPS_SMALL) { hole.dLen = m_TParams.m_dMaxMat ; string sInfo = "Warning in Drilling : Drill bit too short for Hole " + ToString( nCircId) ; m_pMchMgr->SetWarning( 2155, sInfo) ; } // inserisco circonferenza che rappresenta il foro PtrOwner pCrvArc( CreateCurveArc()) ; if ( IsNull( pCrvArc) || ! pCrvArc->Set( hole.ptIni, hole.vtDir, 0.5 * m_TParams.m_dDiam)) return false ; // assegno il versore estrusione e lo spessore pCrvArc->SetExtrusion( hole.vtDir) ; pCrvArc->SetThickness( - hole.dLen) ; // inserisco nel DB int nDriId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, Release( pCrvArc)) ; // assegno nome e colore m_pGeomDB->SetName( nDriId, MCH_PV_CUT) ; m_pGeomDB->SetMaterial( nDriId, LIME) ; // aggiorno numero forature ++ m_nDrillings ; return true ; } //---------------------------------------------------------------------------- bool Drilling::GenerateHoleCl( int nInd, const SelData& nCircId, const string& sPName, int nClId, double dMHOff, const Vector3d& vtA, int nDrillType, INTVECTOR* pvActiveExit, ToolInfo* currTool) { // creo gruppo per geometria di lavorazione del foro int nPathId = m_pGeomDB->AddGroup( GDB_ID_NULL, nClId, Frame3d()) ; if ( nPathId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPathId, sPName + ToString( nInd + 1)) ; m_pGeomDB->SetInfo( nPathId, KEY_IDS, ToString( nCircId)) ; if ( nDrillType == DRILL_TYPE_MULTI_SEL && pvActiveExit != nullptr) m_pGeomDB->SetInfo( nPathId, KEY_DRACEX, ToString( *pvActiveExit)) ; m_pGeomDB->SetMaterial( nPathId, GREEN) ; // recupero il valore di tolleranza sul diametro double dDiamTol = GetHoleDiamToler() ; // recupero parametri dell'utensile principale ToolData currToolData = m_TParams ; if ( nDrillType == DRILL_TYPE_MULTI_SEL) { if ( currTool == nullptr) return false ; currToolData = *currTool->pTool ; } // recupero i dati del foro Hole hole ; if ( ! GetHoleData( nCircId, hole) || ! VerifyDiameter( hole.dDiam, currToolData.m_dDiam, dDiamTol)) { if ( nDrillType == DRILL_TYPE_STD) { m_pGeomDB->Erase( nPathId) ; string sInfo = "Warning in Drilling : Skipped entity " + ToString( nCircId) ; m_pMchMgr->SetWarning( 2151, sInfo) ; } return true ; } // se richiesta inversione e foro passante, provvedo if ( m_Params.m_bInvert) { if ( hole.bBlind) { m_pMchMgr->SetLastError( 2114, "Error in Drilling : blind hole not reversible") ; return false ; } else { hole.ptIni -= hole.vtDir * hole.dThick ; hole.vtDir.Invert() ; } } // se lavorazione del foro non arriva al suo fondo, lo considero cieco if ( hole.dLen < hole.dThick - 10 * EPS_SMALL) hole.bBlind = true ; // imposto elevazione da lunghezza foro con possibilità di sovrascrittura da info double dElev = hole.dLen ; double dMaxElev ; if ( GetValInNotes( m_Params.m_sUserNotes, UN_MAXELEV, dMaxElev) && dElev > dMaxElev) { dElev = dMaxElev ; hole.ptIni += hole.vtDir * ( dElev - hole.dLen) ; hole.dLen = ( hole.dLen > 10 * EPS_SMALL ? max( dElev, 10 * EPS_SMALL) : dElev) ; } // limito lunghezza foro a massima lavorazione della punta ( per utensile corrente ) double dAddLen = ( hole.bBlind ? 0 : m_Params.m_dThroughAddLen) ; if ( ( dElev + dAddLen) > currToolData.m_dMaxMat + EPS_SMALL) { hole.dLen = currToolData.m_dMaxMat + max( hole.dLen - dElev, 0.) ; hole.bBlind = true ; if ( nDrillType == DRILL_TYPE_STD) { string sInfo = "Warning in Drilling : Drill bit too short for Hole " + ToString( nCircId) ; m_pMchMgr->SetWarning( 2155, sInfo) ; } } // verifiche per foro verso il basso m_bAggrBottom = false ; if ( ! VerifyHoleFromBottom( hole, nCircId)) return false ; // assegno il vettore estrazione al gruppo del percorso m_pGeomDB->SetInfo( nPathId, KEY_EXTR, hole.vtDir) ; // assegno i punti di inizio e fine al gruppo del percorso m_pGeomDB->SetInfo( nPathId, KEY_START, hole.ptIni) ; m_pGeomDB->SetInfo( nPathId, KEY_END, hole.ptIni) ; // assegno l'elevazione massima m_pGeomDB->SetInfo( nPathId, KEY_ELEV, hole.dLen) ; // foro normale if ( ( m_Params.m_dStep < EPS_SMALL || m_Params.m_dStep > hole.dLen - 10 * EPS_SMALL) && GetDoubleLastStep() > hole.dLen - 10 * EPS_SMALL) { if ( DoStandardDrilling( hole, nCircId, nPathId, dMHOff, vtA, currToolData)) { // aggiorno numero forature ++ m_nDrillings ; } else { m_pGeomDB->Erase( nPathId) ; string sInfo = "Warning in Drilling : Skipped by Standard Drilling " + ToString( nCircId) ; m_pMchMgr->SetWarning( 2156, sInfo) ; } } else { if ( DoPeckDrilling( hole, nCircId, nPathId, dMHOff, vtA, currToolData)) { // aggiorno numero forature ++ m_nDrillings ; } else { m_pGeomDB->Erase( nPathId) ; string sInfo = "Warning in Drilling : Skipped by Peck Drilling " + ToString( nCircId) ; m_pMchMgr->SetWarning( 2157, sInfo) ; } } // aggiusto i percorsi nel caso di utensili selezionabili con utensile principale non attivo if ( nDrillType == DRILL_TYPE_MULTI_SEL && ! SameTool( currToolData, m_TParams)) { if ( ! AdapthPathToMainTool( nPathId, nCircId, sPName, nClId, dMHOff, vtA, nDrillType, pvActiveExit, &( currToolData))) return false ; } // aggiungo eventuali Warning per fori non lavorati o lavorati parzialmente int nUndrilleds = int( m_vUndrilledId.size()) ; int nPartialDrilled = int( m_vPartialDrilledId.size()) ; if ( nUndrilleds + nPartialDrilled > 0) { string sWarning = "" ; if ( nUndrilleds > 0) { sWarning += "Warning in Drilling : Skipped entities " ; for ( int i = 0 ; i < nUndrilleds ; ++ i) sWarning += ToString( m_vUndrilledId[i]) + ( i != nUndrilleds - 1 ? ", " : "") ; } if ( nPartialDrilled > 0) { if ( nUndrilleds > 0) sWarning += " | " ; sWarning += "Warning in Drilling : Partial Holes " ; for ( int i = 0 ; i < nPartialDrilled ; ++ i) sWarning += "(" + ToString( m_vPartialDrilledId[i].first) + ", Len : " + ToString( m_vPartialDrilledId[i].second) + ")" + ( i != nPartialDrilled - 1 ? ", " : "") ; } m_pMchMgr->SetWarning( 2158, sWarning) ; } return true ; } //--------------------------------------------------------------------------- bool Drilling::AdapthPathToMainTool( int nPathId, const SelData& nCircId, const std::string& sPName, int nClId, double dMHOff, const Vector3d& vtAux, int nDrillType, INTVECTOR* pvActiveExit, ToolData* currToolData) { // controllo di avere delle uscite attive if ( pvActiveExit == nullptr) return false ; // se tool corrente non valido, errore if ( currToolData == nullptr) return false ; // se utensile corrente identico all'utensile principale della lavorazione, non faccio nulla if ( SameTool( *currToolData, m_TParams)) return true ; // inizializzo punti finali degli utensili Point3d ptEndCurrTool = P_INVALID ; Point3d ptEndTParams = P_INVALID ; // vettore direzione utensili Vector3d vtDir ; // gestore degli utensili ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ; if ( pTMgr == nullptr) return false ; // recupero il nome della testa e il numero di uscite string sHead ; m_pMchMgr->GetCurrMachine()->GetCurrHead( sHead) ; int nExitCnt = m_pMchMgr->GetCurrMachine()->GetHeadExitCount( sHead) ; // ricavo gli utensili presenti sulle uscite for ( int nT = 0 ; nT < nExitCnt ; ++ nT) { string sToolName ; if ( ! m_pMchMgr->GetLoadedTool( sHead, nT + 1, sToolName) || sToolName.empty()) continue ; // se presente e valido const ToolData* pTdata = pTMgr->GetTool( sToolName) ; // imposto il tool di riferimento come il tool m_TParams bool bIsTParams = ( pTdata->m_Uuid == m_TParams.m_Uuid) ; bool bIsCurrTool = ( pTdata->m_Uuid == currToolData->m_Uuid) ; if ( bIsTParams || bIsCurrTool) { // recupero i dati dell'uscita Point3d ptExit ; Vector3d vtCurrDir, vtCurrAux ; m_pMchMgr->GetCurrMachine()->GetHeadExitPosDirAux( sHead, nT + 1, ptExit, vtCurrDir, vtCurrAux) ; vtDir = vtCurrDir ; // ho già controllato prima che siano compatibili if ( bIsTParams) ptEndTParams = ptExit - vtCurrDir * pTdata->m_dLen ; else ptEndCurrTool = ptExit - vtCurrDir * pTdata->m_dLen ; } if ( ptEndCurrTool.IsValid() && ptEndTParams.IsValid()) break ; } // se non trovati allora errore if ( ! ptEndCurrTool.IsValid() || ! ptEndTParams.IsValid()) return false ; // creo un sistema di riferimento centrato sul tool corrente Frame3d frCurr ; if ( ! frCurr.Set( ptEndCurrTool, vtDir, vtAux)) return false ; // porto l'altro punto nel riferimento corrente ptEndTParams.ToLoc( frCurr) ; // ricavo il vettore traslazione Vector3d vtTrasl = ( ptEndTParams - ORIG) ; vtTrasl.ToGlob( frCurr) ; // scorro le entità nel gruppo di lavorazione corrente Hole myHole ; if ( ! GetHoleData( nCircId, myHole)) return false ; int nId = m_pGeomDB->GetFirstInGroup( nPathId) ; while ( nId != GDB_ID_NULL) { // recupero l'oggetto CamData auto* myCamDataObj = GetCamData( m_pGeomDB->GetUserObj( nId)) ; myCamDataObj->Translate( vtTrasl) ; // recupero l'oggetto geometrico auto* myGeoObj = m_pGeomDB->GetGeoObj( nId) ; myGeoObj->Translate( vtTrasl) ; // passo all'indice successivo nId = m_pGeomDB->GetNext( nId) ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::GenerateHoleRegionPv( int nFirstId, int nCount, int nPvId) { // raggio dei fori double dRad = m_TParams.m_dDiam / 2 ; // extra raggio double dExtraR = GetExtraROnDrillRegion() ; // gruppo di inserimento int nGroupId = m_pGeomDB->GetLastGroupInGroup( nPvId) ; // creo polyline che unisce i centri PolyLine PL ; for ( int i = 0 ; i < nCount ; ++ i) { int nId = nFirstId + i ; const ICurveArc* pArc = GetCurveArc( m_pGeomDB->GetGeoObj( nId)) ; if ( pArc == nullptr) return false ; PL.AddUPoint( i, pArc->GetCenter()) ; } // calcolo la regione ISurfFlatRegion* pSfr = nullptr ; // se polilinea con almeno due punti if ( PL.GetPointNbr() > 1) { // ne derivo una curva composita PtrOwner pCompo1( CreateCurveComposite()) ; if ( IsNull( pCompo1) || ! pCompo1->FromPolyLine( PL)) return false ; // calcolo la regione pSfr = GetSurfFlatRegionFromFatCurve( Release( pCompo1), dRad + dExtraR, false, true) ; if ( pSfr == nullptr) return false ; } // altrimenti else { // approssimo l'unico foro pSfr = GetSurfFlatRegionDisk( dRad + dExtraR) ; if ( pSfr == nullptr) return false ; Point3d ptCen ; PL.GetFirstPoint( ptCen) ; pSfr->Translate( ptCen - ORIG) ; } // inserisco la regione nel DB int nRId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nGroupId, pSfr) ; if ( nRId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nRId, MCH_PV_RCUT) ; m_pGeomDB->SetMaterial( nRId, INVISIBLE) ; // la copio anche come regione ridotta int nRrId = m_pGeomDB->Copy( nRId, GDB_ID_NULL, nGroupId) ; if ( nRrId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nRrId, MCH_PV_RRCUT) ; m_pGeomDB->SetMaterial( nRrId, INVISIBLE) ; return true ; } //---------------------------------------------------------------------------- bool Drilling::VerifyDiameter( double dHdiam, double dTdiam, double dDiamTol) { // imposto minima tolleranza if ( abs( dDiamTol) < 10 * EPS_SMALL) dDiamTol = 10 * EPS_SMALL ; // se tolleranza in più o in meno if ( dDiamTol > 0) return ( abs( dHdiam - dTdiam) < dDiamTol) ; // altrimenti tolleranza solo in meno return ( dTdiam < dHdiam + 10 * EPS_SMALL && dTdiam > dHdiam + dDiamTol - 10 * EPS_SMALL) ; } //---------------------------------------------------------------------------- bool Drilling::GetParam( int nType, bool& bVal) const { switch ( nType) { case MPA_INVERT : bVal = m_Params.m_bInvert ; return true ; } bVal = false ; return false ; } //---------------------------------------------------------------------------- bool Drilling::GetParam( int nType, int& nVal) const { switch ( nType) { case MPA_TYPE : nVal = MT_DRILLING ; return true ; case MPA_SUBTYPE : nVal = m_Params.m_nSubType ; return true ; case MPA_SCC : nVal = m_Params.m_nSolCh ; return true ; } nVal = 0 ; return false ; } //---------------------------------------------------------------------------- bool Drilling::GetParam( int nType, double& dVal) const { switch ( nType) { case MPA_SPEED : dVal = GetSpeed() ; return true ; case MPA_FEED : dVal = GetFeed() ; return true ; case MPA_STARTFEED : dVal = GetStartFeed() ; return true ; case MPA_ENDFEED : dVal = GetEndFeed() ; return true ; case MPA_TIPFEED : dVal = GetTipFeed() ; return true ; case MPA_STARTPOS : dVal = m_Params.m_dStartPos ; return true ; case MPA_STARTSLOWLEN : dVal = m_Params.m_dStartSlowLen ; return true ; case MPA_ENDSLOWLEN : dVal = m_Params.m_dEndSlowLen ; return true ; case MPA_THROUADDLEN : dVal = m_Params.m_dThroughAddLen ; return true ; case MPA_STEP : dVal = m_Params.m_dStep ; return true ; case MPA_RETURNPOS : dVal = m_Params.m_dReturnPos ; return true ; case MPA_STARTADDLEN : dVal = m_Params.m_dStartAddLen ; return true ; case MPA_ENDADDLEN : dVal = m_Params.m_dEndAddLen ; return true ; } dVal = 0 ; return false ; } //---------------------------------------------------------------------------- bool Drilling::GetParam( int nType, string& sVal) const { switch ( nType) { case MPA_NAME : sVal = m_Params.m_sName ; return true ; case MPA_TOOL : sVal = m_Params.m_sToolName ; return true ; case MPA_DEPTH_STR : sVal = m_Params.m_sDepth ; return true ; case MPA_TUUID : sVal = ToString( m_Params.m_ToolUuid) ; return true ; case MPA_UUID : sVal = ToString( m_Params.m_Uuid) ; return true ; case MPA_SYSNOTES : sVal = m_Params.m_sSysNotes ; return true ; case MPA_USERNOTES : sVal = m_Params.m_sUserNotes ; return true ; case MPA_OVERLAP_STR : sVal = m_Params.m_sOverlap ; return true ; case MPA_OFFSET_STR : sVal = m_Params.m_sOffset ; return true ; case MPA_INITANGS : sVal = m_Params.m_sInitAngs ; return true ; case MPA_BLOCKEDAXIS : sVal = m_Params.m_sBlockedAxis ; return true ; } sVal = "" ; return false ; } //---------------------------------------------------------------------------- const ToolData& Drilling::GetToolData( void) const { return m_TParams ; } //---------------------------------------------------------------------------- bool Drilling::UpdateToolData( void) { // recupero il gestore DB utensili della macchina corrente ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ; if ( pTMgr == nullptr) return false ; // recupero l'utensile nel DB utensili (se fallisce con UUID provo con il nome) const ToolData* pTdata = pTMgr->GetTool( m_Params.m_ToolUuid) ; if ( pTdata == nullptr) { pTdata = pTMgr->GetTool( m_Params.m_sToolName) ; if ( pTdata == nullptr) return false ; m_Params.m_ToolUuid = m_TParams.m_Uuid ; } // salvo posizione TC, testa e uscita originali string sOrigTcPos = m_TParams.m_sTcPos ; string sOrigHead = m_TParams.m_sHead ; int nOrigExit = m_TParams.m_nExit ; // verifico se sono diversi (ad esclusione di nome, posizione TC, testa e uscita) bool bChanged = ( ! SameTool( m_TParams, *pTdata, false)) ; // aggiorno comunque i parametri m_TParams = *pTdata ; // se definito attrezzaggio, aggiorno i parametri che ne possono derivare string sTcPos ; string sHead ; int nExit ; if ( m_pMchMgr->GetCurrSetupMgr().GetToolData( m_TParams.m_sName, sTcPos, sHead, nExit)) { if ( sOrigTcPos != sTcPos || sOrigHead != sHead || nOrigExit != nExit) bChanged = true ; m_TParams.m_sTcPos = sTcPos ; m_TParams.m_sHead = sHead ; m_TParams.m_nExit = nExit ; } else { if ( sOrigTcPos != pTdata->m_sTcPos || sOrigHead != pTdata->m_sHead || nOrigExit != pTdata->m_nExit) bChanged = true ; } // eventuali segnalazioni if ( ! EqualNoCase( m_Params.m_sToolName, m_TParams.m_sName)) { string sInfo = "Warning in Drilling : tool name changed (" + m_Params.m_sToolName + "->" + m_TParams.m_sName + ")" ; m_pMchMgr->SetWarning( 2153, sInfo) ; m_Params.m_sToolName = m_TParams.m_sName ; } if ( bChanged) { string sInfo = "Warning in Drilling : tool data changed (" + m_Params.m_sToolName + ")" ; m_pMchMgr->SetWarning( 2154, sInfo) ; } // se modificato, aggiusto lo stato if ( bChanged) m_nStatus = MCH_ST_TO_VERIFY ; return true ; } //---------------------------------------------------------------------------- bool Drilling::GetGeometry( SELVECTOR& vIds) const { // restituisco l'elenco delle entità vIds = m_vId ; return true ; } //---------------------------------------------------------------------------- bool Drilling::GetSkippedGeometry( SELVECTOR& vIds) const { // pulisco elenco entità non lavorate vIds.clear() ; // inserisco le entità non lavorate for ( const auto& nId : m_vUndrilledId) vIds.emplace_back( nId, SEL_SUB_ALL) ; return true ; } //---------------------------------------------------------------------------- bool Drilling::GetHoleData( SelData Id, Hole& hole) { PtrOwner pMyArc ; // deve essere un arco o una composita formata da un arco const ICurveArc* pArc = GetCurveArc( m_pGeomDB->GetGeoObj( Id.nId)) ; if ( pArc == nullptr) { const ICurveComposite* pCompo = GetCurveComposite( m_pGeomDB->GetGeoObj( Id.nId)) ; Point3d ptCen ; Vector3d vtN ; double dRad ; bool bCCW ; if ( pCompo == nullptr || ! pCompo->IsACircle( 100 * EPS_SMALL, ptCen, vtN, dRad, bCCW)) return false ; pMyArc.Set( CreateCurveArc()) ; if ( IsNull( pMyArc) || ! pMyArc->Set( ptCen, vtN, dRad)) return false ; Vector3d vtExtr ; pCompo->GetExtrusion( vtExtr) ; pMyArc->SetExtrusion( vtExtr) ; double dThick = 0 ; pCompo->GetThickness( dThick) ; pMyArc->SetThickness( dThick) ; pArc = pMyArc ; } // ne recupero il riferimento globale Frame3d frGlob ; if ( ! m_pGeomDB->GetGlobFrame( Id.nId, frGlob)) return false ; // deve avere estrusione Vector3d vtExtr ; if ( ! pArc->GetExtrusion( vtExtr)) return false ; if ( vtExtr.IsSmall()) vtExtr = pArc->GetNormVersor() ; // recupero spessore double dThick = 0 ; pArc->GetThickness( dThick) ; // se diretto come asse Z, recupero distanza da fondo dei grezzi interessati dal foro double dRbDist = 0 ; Vector3d vtExtrG = vtExtr ; vtExtrG.ToGlob( frGlob) ; if ( vtExtrG.IsZplus()) GetDistanceFromRawBottom( m_nPhase, Id.nId, EPS_SMALL, dRbDist) ; // valuto l'espressione dell'affondamento ExeLuaSetGlobNumVar( "TH", abs( dThick)) ; ExeLuaSetGlobNumVar( "RB", dRbDist) ; double dDepth ; string sMyDepth = m_Params.m_sDepth ; if ( ! ExeLuaEvalNumExpr( ToUpper( sMyDepth), &dDepth)) { m_pMchMgr->SetLastError( 2104, "Error in Drilling : Depth not computable") ; return false ; } if ( abs( dDepth) < EPS_SMALL) { m_pMchMgr->SetLastError( 2105, "Error in Drilling : Drill with zero depth") ; return false ; } // assegno Id hole.nOriId = Id.nId ; // ne recupero il diametro hole.dDiam = 2 * pArc->GetRadius() ; // ne recupero il centro hole.ptIni = pArc->GetCenter() + ( dThick > 0 ? vtExtr * dThick : V_NULL) ; hole.ptIni.ToGlob( frGlob) ; // ne recupero versore direzione e lunghezze hole.vtDir = vtExtr ; hole.vtDir.ToGlob( frGlob) ; hole.vtDir.Normalize() ; hole.dThick = abs( dThick) ; hole.dLen = abs( dDepth) ; // per default è cieco hole.bBlind = true ; // se dichiarato passante o no (1/0) int nOpen ; if ( GetValInNotes( m_Params.m_sUserNotes, UN_OPEN, nOpen)) hole.bBlind = ( nOpen == 0) ; // se verticale ed arriva fino al fondo grezzo, allora passante else if ( hole.vtDir.IsZplus() && hole.dThick > dRbDist - EPS_SMALL) hole.bBlind = false ; // altrimenti determino la distanza del fondo del foro dal bordo opposto del grezzo else { const double BLIND_TOL = 10 ; double dBackElev ; if ( GetElevation( m_nPhase, hole.ptIni, - hole.vtDir, dBackElev) && ( dBackElev - hole.dThick) <= BLIND_TOL) hole.bBlind = false ; } return true ; } //---------------------------------------------------------------------------- bool Drilling::VerifyHoleFromBottom( const Hole& hole, SelData Id) { // se non è foro dal basso in alto, esco if ( hole.vtDir.z > MIN_ZDIR_TOP_TOOL) return true ; // se c'è testa non dall'alto o tavola basculante, esco if ( ! m_bAboveHead || m_bTiltingTab) return true ; // recupero dati di eventuale rinvio da sotto if ( ! GetAggrBottomData( m_TParams.m_sHead, m_AggrBottom) || m_AggrBottom.nType == 0) { string sOut = "Error in Drilling : Entity " + ToString( Id) + " skipped because missing aggregate from bottom" ; m_pMchMgr->SetLastError( 2106, sOut) ; return false ; } // calcolo la distanza minima del punto dal contorno del grezzo double dDist ; Vector3d vtDir ; if ( ! GetMinDistanceFromRawSide( m_nPhase, hole.ptIni, 0, m_AggrBottom.vtMDir, MCH_AGB_DELTAMAX_MDIR, dDist, vtDir) || dDist > m_AggrBottom.dDMax) { string sOut = "Error in Drilling : Entity " + ToString( Id) + " skipped because too far from part sides" ; m_pMchMgr->SetLastError( 2107, sOut) ; return false ; } // assegno direzione di accesso e segnalazione di utilizzo aggregato da sotto m_vtAggrBottom = vtDir ; m_bAggrBottom = true ; m_dDistBottom = dDist ; return true ; } //---------------------------------------------------------------------------- bool Drilling::VerifyParallelDrilling( int nDouble, const Hole& hole) { // verifico se lavorazione in doppio valida if ( nDouble != 1 && nDouble != 2 && nDouble != 3) return false ; // se la macchina non presenta nel file .ini la possibilità di lavorazione in doppio parallela, esco Machine* pMch = m_pMchMgr->GetCurrMachine() ; if ( pMch == nullptr) return false ; string sMachIni = pMch->GetMachineDir() + "\\" + pMch->GetMachineName() + ".ini" ; int nDrillingDouble = GetPrivateProfileInt( MACHININGS_SEC.c_str(), DRILLING_PARALLEL_KEY.c_str(), 0, sMachIni.c_str()) ; if ( nDrillingDouble != 1) return false ; // recupero il piano di Mirror Point3d ptOn ; Vector3d vtNorm ; if ( ! CalcMirrorPlaneByDouble( nDouble, m_Params.m_sUserNotes, ptOn, vtNorm)) return false ; Plane3d plMirror ; if ( ! plMirror.Set( ptOn, vtNorm)) return false ; // verifico subito che la normale del piano si trovi entro un grado rispetto alla direzione del foro ( tolleranza da .BTL) if ( abs( hole.vtDir * vtNorm) < cos( ( 1. - EPS_ANG_SMALL) * DEGTORAD)) return false ; // se il punto finale del foro si trova nel semipiano positivo di Mirroring e sufficientemente distante da esso, non eseguo // lavorazione in parallelo Point3d ptHoleEnd = hole.ptIni - hole.dLen * hole.vtDir ; const double SAFE_DIST_TOL = 5. ; double dDist = ( ptHoleEnd - ptOn) * vtNorm ; if ( dDist < EPS_SMALL) return true ; double dSafeTipDist = m_TParams.m_dTLen - m_TParams.m_dLen ; if ( dDist < dSafeTipDist + SAFE_DIST_TOL) return true ; return false ; } //---------------------------------------------------------------------------- bool Drilling::DoStandardDrilling( const Hole& hole, SelData Id, int nPathId, double dMHOff, const Vector3d& vtA, const ToolData& currToolData) { // aggiusto alcuni parametri del ciclo di foratura double dStartSlowLen = abs( m_Params.m_dStartSlowLen) ; if ( abs( currToolData.m_dStartFeed - currToolData.m_dFeed) < EPS_SMALL) dStartSlowLen = 0 ; double dEndSlowLen = ( hole.bBlind ? 0 : abs( m_Params.m_dEndSlowLen)) ; if ( abs( currToolData.m_dTipFeed - currToolData.m_dFeed) < EPS_SMALL) dEndSlowLen = 0 ; if ( ( dStartSlowLen + dEndSlowLen) > hole.dLen) { dStartSlowLen = dStartSlowLen / ( dStartSlowLen + dEndSlowLen) * hole.dLen ; dEndSlowLen = hole.dLen - dStartSlowLen ; } double dAddLen = ( hole.bBlind ? 0 : m_Params.m_dThroughAddLen) ; bool bStartSlow = ( dStartSlowLen > EPS_SMALL) ; bool bStd = ( ( hole.dLen - dStartSlowLen - dEndSlowLen) > EPS_SMALL) ; bool bEndSlow = ( dEndSlowLen > EPS_SMALL) ; // determino l'elevazione double dElev = 0 ; GetElevation( m_nPhase, hole.ptIni, hole.vtDir, currToolData.m_dDiam / 2, hole.vtDir, dElev) ; // determino alcune caratteristiche dell'utensile double dTExtrLen = max( 0.0, currToolData.m_dTLen - currToolData.m_dLen) ; // necessità di spezzatura per robot bool bSplitArcs = GetSplitArcs( V_NULL) ; // imposto dati comuni SetPathId( nPathId) ; SetToolDir( hole.vtDir) ; // 1 -> punto approccio (se con aggregato da sotto è una sequenza di approccio) SetFlag( 1) ; double dSafeZ = GetSafeZ() ; double dSafeAggrBottZ = GetSafeAggrBottZ() ; double dAppr = ( m_bAggrBottom ? dSafeAggrBottZ : dSafeZ) ; Point3d ptP1 = hole.ptIni + hole.vtDir * ( dAppr + dElev + dTExtrLen + dMHOff) ; if ( m_bAggrBottom) { Point3d ptP0 = ptP1 + m_vtAggrBottom * ( m_dDistBottom + m_AggrBottom.dEncH + dSafeZ) ; // se rinvio da sotto che richiede speciale rotazione if ( m_AggrBottom.nType == 1) { // punto ruotato Point3d ptP00 = ptP0 + Z_AX * ( m_AggrBottom.dEncV + currToolData.m_dLen + dAppr + dTExtrLen) ; Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidStart( ptP00, MCH_CL_AGB_DWN) == GDB_ID_NULL) return false ; // vado al punto standard SetAuxDir( m_vtAggrBottom) ; SetFlag( 0) ; if ( AddRapidMove( ptP0, bSplitArcs, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; } // se altrimenti con rotazione per minimizzare la sporgenza else if ( m_AggrBottom.nType == 3) { // punto standard ruotato Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidStart( ptP0, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; // la rotazione viene eseguita nel movimento successivo al punto sopra l'inizio lavorazione SetAuxDir( m_vtAggrBottom) ; SetFlag( 0) ; } // altrimenti rinvio normale else { SetAuxDir( m_vtAggrBottom) ; if ( AddRapidStart( ptP0, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; SetFlag( 0) ; } if ( AddRapidMove( ptP1, bSplitArcs) == GDB_ID_NULL) return false ; } else { if ( ! vtA.IsSmall()) SetAuxDir( vtA) ; if ( AddRapidStart( ptP1) == GDB_ID_NULL) return false ; } SetFlag( 0) ; // 2 -> punto fuori (se diverso dal precedente) if ( m_Params.m_dStartPos < dAppr) { Point3d ptP2 = hole.ptIni + hole.vtDir * ( m_Params.m_dStartPos + dTExtrLen) ; if ( AddRapidMove( ptP2, bSplitArcs) == GDB_ID_NULL) return false ; } // 3 -> punto termine velocità ridotta iniziale (se previsto) if ( bStartSlow) { SetFeed( GetStartFeed()) ; if ( ! bStd && ! bEndSlow) SetFlag( 101) ; // fondo del foro Point3d ptP3 = hole.ptIni - hole.vtDir * dStartSlowLen ; if ( ! bStd && ! bEndSlow) ptP3 -= hole.vtDir * dAddLen ; if ( AddLinearMove( ptP3, bSplitArcs) == GDB_ID_NULL) return false ; } // 4 -> punto termine velocità standard (se risulta) if ( bStd) { SetFeed( GetFeed()) ; if ( ! bEndSlow) SetFlag( 101) ; // fondo del foro Point3d ptP4 = hole.ptIni - hole.vtDir * ( hole.dLen - dEndSlowLen) ; if ( ! bEndSlow) ptP4 -= hole.vtDir * dAddLen ; if ( AddLinearMove( ptP4, bSplitArcs) == GDB_ID_NULL) return false ; } // 5 -> punto termine velocità finale ridotta (se previsto) if ( bEndSlow) { SetFeed( GetTipFeed()) ; SetFlag( 101) ; // fondo del foro Point3d ptP5 = hole.ptIni - hole.vtDir * ( hole.dLen + dAddLen) ; if ( AddLinearMove( ptP5, bSplitArcs) == GDB_ID_NULL) return false ; } // 6 -> ritorno all'approccio del foro SetFeed( GetEndFeed()) ; SetFlag( 104) ; // risalita sopra il foro if ( AddLinearMove( ptP1, bSplitArcs) == GDB_ID_NULL) return false ; // 7 -> punto fuori (se uso aggregato da sotto) if ( m_bAggrBottom) { // se con rotazione per minimizzare la sporgenza if ( m_AggrBottom.nType == 3) { // imposto rotazione su punto standard Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; } SetFlag( 0) ; // vado al punto Point3d ptP0 = ptP1 + m_vtAggrBottom * ( m_dDistBottom + m_AggrBottom.dEncH + dSafeZ) ; if ( AddRapidMove( ptP0, bSplitArcs, MCH_CL_AGB_OUT) == GDB_ID_NULL) return false ; // se rinvio da sotto che richiede speciale rotazione if ( m_AggrBottom.nType == 1) { Point3d ptP00 = ptP0 + Z_AX * ( m_AggrBottom.dEncV + currToolData.m_dLen + dAppr + dTExtrLen) ; Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidMove( ptP00, bSplitArcs, MCH_CL_AGB_UP) == GDB_ID_NULL) return false ; } } // reset dati di movimento ResetMoveData() ; return true ; } //---------------------------------------------------------------------------- bool Drilling::DoPeckDrilling( const Hole& hole, SelData Id, int nPathId, double dMHOff, const Vector3d& vtA, const ToolData& currToolData) { // aggiusto alcuni parametri del ciclo di foratura double dStartSlowLen = abs( m_Params.m_dStartSlowLen) ; if ( abs( currToolData.m_dStartFeed - currToolData.m_dFeed) < EPS_SMALL) dStartSlowLen = 0 ; double dEndSlowLen = ( hole.bBlind ? 0 : abs( m_Params.m_dEndSlowLen)) ; if ( abs( currToolData.m_dTipFeed - currToolData.m_dFeed) < EPS_SMALL) dEndSlowLen = 0 ; if ( ( dStartSlowLen + dEndSlowLen) > hole.dLen) { dStartSlowLen = dStartSlowLen / ( dStartSlowLen + dEndSlowLen) * hole.dLen ; dEndSlowLen = hole.dLen - dStartSlowLen ; } double dStdLen = hole.dLen - dStartSlowLen - dEndSlowLen ; double dAddLen = ( hole.bBlind ? 0 : m_Params.m_dThroughAddLen) ; double dReturnPos = m_Params.m_dReturnPos ; bool bStartSlow = ( dStartSlowLen > EPS_SMALL) ; bool bStd = ( dStdLen > EPS_SMALL) ; bool bEndSlow = ( dEndSlowLen > EPS_SMALL) ; // determino l'elevazione double dElev = 0 ; GetElevation( m_nPhase, hole.ptIni, hole.vtDir, currToolData.m_dDiam / 2, hole.vtDir, dElev) ; // determino alcune caratteristiche dell'utensile double dTExtrLen = max( 0.0, currToolData.m_dTLen - currToolData.m_dLen) ; // necessità di spezzatura per robot bool bSplitArcs = GetSplitArcs( V_NULL) ; // imposto dati comuni SetPathId( nPathId) ; SetToolDir( hole.vtDir) ; // 1 -> punto approccio SetFlag( 1) ; double dSafeZ = GetSafeZ() ; double dSafeAggrBottZ = GetSafeAggrBottZ() ; double dAppr = ( m_bAggrBottom ? dSafeAggrBottZ : dSafeZ) ; Point3d ptP1 = hole.ptIni + hole.vtDir * ( dAppr + dElev + dTExtrLen + dMHOff) ; if ( m_bAggrBottom) { Point3d ptP0 = ptP1 + m_vtAggrBottom * ( m_dDistBottom + m_AggrBottom.dEncH + dSafeZ) ; // se rinvio da sotto che richiede speciale rotazione if ( m_AggrBottom.nType == 1) { // punto ruotato Point3d ptP00 = ptP0 + Z_AX * ( m_AggrBottom.dEncV + currToolData.m_dLen + dAppr + dTExtrLen) ; Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidStart( ptP00, MCH_CL_AGB_DWN) == GDB_ID_NULL) return false ; // vado al punto standard SetFlag( 0) ; SetAuxDir( m_vtAggrBottom) ; if ( AddRapidMove( ptP0, bSplitArcs, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; } // se altrimenti con rotazione per minimizzare la sporgenza else if ( m_AggrBottom.nType == 3) { // punto standard ruotato Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidStart( ptP0, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; // la rotazione viene eseguita nel movimento successivo al punto sopra l'inizio lavorazione SetAuxDir( m_vtAggrBottom) ; SetFlag( 0) ; } // altrimenti rinvio normale else { SetAuxDir( m_vtAggrBottom) ; if ( AddRapidStart( ptP0, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; SetFlag( 0) ; } if ( AddRapidMove( ptP1, bSplitArcs) == GDB_ID_NULL) return false ; } else { if ( ! vtA.IsSmall()) SetAuxDir( vtA) ; if ( AddRapidStart( ptP1) == GDB_ID_NULL) return false ; } SetFlag( 0) ; // 2 -> punto fuori (se diverso dal precedente) if ( m_Params.m_dStartPos < dAppr) { Point3d ptP2 = hole.ptIni + hole.vtDir * ( m_Params.m_dStartPos + dTExtrLen) ; if ( AddRapidMove( ptP2, bSplitArcs) == GDB_ID_NULL) return false ; } // parametri per foro in doppio int nDouble = GetDoubleType( m_Params.m_sUserNotes) ; bool bDouble = ( nDouble != 0) ; bool bDoubleParallel = false ; if ( bDouble) bDoubleParallel = VerifyParallelDrilling( nDouble, hole) ; double dDoubleLastStep = GetDoubleLastStep() ; // ciclo di affondamento a step const double MIN_STEP = 1 ; const double APPR_STEP = 1 ; const double MIN_MOVE = 1 ; double dOrigStep = max( m_Params.m_dStep, MIN_STEP) ; double dSteppedLen = ( bDouble ? hole.dLen - dDoubleLastStep : hole.dLen) ; int nStep = int( ceil( dSteppedLen / dOrigStep)) ; double dStep = dSteppedLen / nStep ; if ( bDouble) ++ nStep ; double dLastStep = ( bDouble ? dDoubleLastStep : dStep) ; if ( dReturnPos < - dStep + APPR_STEP + MIN_MOVE) dReturnPos = - dStep + APPR_STEP + MIN_MOVE ; double dCurrLen = 0 ; for ( int i = 1 ; i <= nStep ; ++ i) { // se non è primo step faccio retrazione e riaffondo if ( i != 1) { // retrazione SetFeed( GetEndFeed()) ; SetFlag( 103) ; // punto di scarico truciolo Point3d ptPr = hole.ptIni + hole.vtDir * dReturnPos ; if ( AddLinearMove( ptPr, bSplitArcs) == GDB_ID_NULL) return false ; // riaffondo SetFeed( GetEndFeed()) ; SetFlag( 0) ; Point3d ptPa = hole.ptIni - hole.vtDir * ( dCurrLen - APPR_STEP) ; if ( AddLinearMove( ptPa, bSplitArcs) == GDB_ID_NULL) return false ; } // lunghezza di fine step double dEndLen = dCurrLen + ( i == nStep ? dLastStep : dStep) ; // 3 -> punto termine velocità ridotta iniziale (se previsto) if ( bStartSlow && ( i == 1 || dCurrLen < dStartSlowLen + EPS_SMALL)) { // lunghezza di esecuzione double dLen = min( dStartSlowLen, dEndLen) ; // determino se arrivo in fondo al foro bool bHoleEnd = ( ! bStd && ! bEndSlow && dLen > hole.dLen - EPS_SMALL) ; // determino se arrivo in fondo allo step bool bStepEnd = ( dLen > dEndLen - EPS_SMALL) ; // assegno parametri SetFeed( GetStartFeed()) ; if ( bHoleEnd) SetFlag( 101) ; // fondo del foro else if ( bStepEnd) SetFlag( 102) ; // fondo dello step // movimento Point3d ptP3 = hole.ptIni - hole.vtDir * dLen ; if ( bHoleEnd) ptP3 -= hole.vtDir * dAddLen ; if ( bDoubleParallel && i == nStep) { SetFlag( 105) ; // movimento in doppio parallelo if ( AddLinearMove( ptP3, bSplitArcs, MCH_CL_PARALLEL_DBL) == GDB_ID_NULL) return false ; } else { if ( AddLinearMove( ptP3, bSplitArcs) == GDB_ID_NULL) return false ; } // aggiorno posizione e verifico se step completato dCurrLen = dLen ; if ( bHoleEnd || bStepEnd) continue ; } // 4 -> punto termine velocità standard (se risulta) if ( bStd && dCurrLen < dStartSlowLen + dStdLen + EPS_SMALL) { // lunghezza di esecuzione double dLen = min( hole.dLen - dEndSlowLen, dEndLen) ; // determino se arrivo in fondo al foro bool bHoleEnd = ( ! bEndSlow && dLen > hole.dLen - EPS_SMALL) ; // determino se arrivo in fondo allo step bool bStepEnd = ( dLen > dEndLen - EPS_SMALL) ; // assegno parametri SetFeed( GetFeed()) ; if ( bHoleEnd) SetFlag( 101) ; // fondo del foro else if ( bStepEnd) SetFlag( 102) ; // fondo dello step // movimento Point3d ptP4 = hole.ptIni - hole.vtDir * dLen ; if ( bHoleEnd) ptP4 -= hole.vtDir * dAddLen ; if ( bDoubleParallel && i == nStep) { SetFlag( 105) ; // movimento in doppio parallelo if ( AddLinearMove( ptP4, bSplitArcs, MCH_CL_PARALLEL_DBL) == GDB_ID_NULL) return false ; } else { if ( AddLinearMove( ptP4, bSplitArcs) == GDB_ID_NULL) return false ; } // aggiorno posizione e verifico se step completato dCurrLen = dLen ; if ( bHoleEnd || bStepEnd) continue ; } // 5 -> punto termine velocità finale ridotta (se previsto) if ( bEndSlow) { // lunghezza di esecuzione double dLen = dEndLen ; // determino se arrivo in fondo al foro bool bHoleEnd = ( dLen > hole.dLen - EPS_SMALL) ; // sono sempre in fondo allo step // assegno parametri SetFeed( GetTipFeed()) ; if ( bHoleEnd) SetFlag( 101) ; // fondo del foro else SetFlag( 102) ; // fondo dello step // movimento Point3d ptP5 = hole.ptIni - hole.vtDir * dLen ; if ( bHoleEnd) ptP5 -= hole.vtDir * dAddLen ; if ( bDoubleParallel && i == nStep) { SetFlag( 105) ; // movimento in doppio parallelo if ( AddLinearMove( ptP5, bSplitArcs, MCH_CL_PARALLEL_DBL) == GDB_ID_NULL) return false ; } else { if ( AddLinearMove( ptP5, bSplitArcs) == GDB_ID_NULL) return false ; } // aggiorno posizione dCurrLen = dLen ; } } // 6 -> ritorno all'approccio del foro SetFeed( GetEndFeed()) ; if ( bDoubleParallel) { SetFlag( 105) ; // movimento in doppio parallelo // aggiungo risalita aggiuntiva per lavorazione in doppio pari a due volte il LastStep Point3d ptEnd ; GetCurrPos( ptEnd) ; if ( AddLinearMove( ptEnd + min( dLastStep, dCurrLen) * hole.vtDir, bSplitArcs, MCH_CL_PARALLEL_DBL) == GDB_ID_NULL) return false ; SetFeed( GetFeed()) ; GetCurrPos( ptEnd) ; if ( AddLinearMove( ptEnd + min( dLastStep, dCurrLen) * hole.vtDir, bSplitArcs, MCH_CL_PARALLEL_DBL) == GDB_ID_NULL) return false ; SetFeed( GetEndFeed()) ; // aggiungo discensa di LastStep per simmetria con secondo utensile GetCurrPos( ptEnd) ; if ( AddLinearMove( ptEnd - dLastStep * hole.vtDir, bSplitArcs, MCH_CL_PARALLEL_DBL) == GDB_ID_NULL) return false ; } SetFlag( 104) ; // risalita sopra il foro if ( AddLinearMove( ptP1, bSplitArcs) == GDB_ID_NULL) return false ; // 7 -> punto fuori (se uso aggregato da sotto) if ( m_bAggrBottom) { // se con rotazione per minimizzare la sporgenza if ( m_AggrBottom.nType == 3) { // imposto rotazione su punto standard Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; } // vado al punto SetFlag( 0) ; Point3d ptP0 = ptP1 + m_vtAggrBottom * ( m_dDistBottom + m_AggrBottom.dEncH + dSafeZ) ; if ( AddRapidMove( ptP0, bSplitArcs, MCH_CL_AGB_OUT) == GDB_ID_NULL) return false ; // se rinvio da sotto che richiede speciale rotazione if ( m_AggrBottom.nType == 1) { Point3d ptP00 = ptP0 + Z_AX * ( m_AggrBottom.dEncV + currToolData.m_dLen + dAppr + dTExtrLen) ; Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidMove( ptP00, bSplitArcs, MCH_CL_AGB_UP) == GDB_ID_NULL) return false ; } } // reset dati di movimento ResetMoveData() ; return true ; } //---------------------------------------------------------------------------- double Drilling::GetDoubleLastStep( void) { // se non è foratura in doppio, restituisco valore molto grande const double FALSE_LASTSTEP = 10000 ; if ( GetDoubleType( m_Params.m_sUserNotes) == 0) return FALSE_LASTSTEP ; // recupero valore const double MIN_LASTSTEP = 15 ; double dDoubleLastStep = m_Params.m_dStep ; if ( GetValInNotes( m_Params.m_sUserNotes, UN_LASTSTEP, dDoubleLastStep)) dDoubleLastStep = max( dDoubleLastStep, MIN_LASTSTEP) ; return dDoubleLastStep ; } //---------------------------------------------------------------------------- // Debug Functions //---------------------------------------------------------------------------- void Drilling::PrintConfigs( const VECTORHOLE& vHoles) { // Matrice ( #Configurazioni, #Fori ) // Ogni cella contiene ( Indice Foro in cui inserire il tool Main, Indice del Tool che lavoro il foro corrente, vtAux ) // Esempio ( Cella )_ij = ( A, B, vtAux)_ij : // Nella configurazione i-esima il foro j esimo è lavorato dal tool B inserendo il tool principale nel foro A // mantenendo un vettore ausiliario pari a vtAux int nSpace = 25 ; // riga dei fori string sHoleRow = " " ; // 12 spazi for ( int i = 0 ; i < int( vHoles.size()) ; ++ i) { sHoleRow.append( "(" + ToString( i) + ")") ; for ( int j = sHoleRow.length() ; j < ( i + 1) * nSpace + 12 ; ++ j) sHoleRow.append( " ") ; } LOG_ERROR( GetEMkLogger(), sHoleRow.c_str()) ; // scorro le configurazioni for ( int nC = 0 ; ! vHoles.empty() && nC < int( vHoles[0].vIndTools.size()) ; ++ nC) { string sRow = "(" + ToString( nC) + ")" ; if ( vHoles[0].vIndMainTool.empty()) sRow.append( " ->") ; else sRow.append( "[" + ToString( vHoles[0].vIndMainTool[nC]) + "]->") ; for ( int j = sRow.length() ; j < 12 ; ++ j) sRow.append( " ") ; for ( int nH = 0 ; nH < int( vHoles.size()) ; ++ nH) { string sCell = "(" ; if ( vHoles[nH].vIndTools[nC] != IND_TOOL_INVALID && vHoles[nH].vToolHole[nC] != IND_TOOL_INVALID && vHoles[nH].vVtAux[nC].IsValid()) { sCell.append( ( ToString( vHoles[nH].vToolHole[nC]) + ", " + ToString( vHoles[nH].vIndTools[nC]) + ", {" + ToString( vHoles[nH].vVtAux[nC].x, 3) + ", " + ToString( vHoles[nH].vVtAux[nC].y, 3) + ", " + ToString( vHoles[nH].vVtAux[nC].z, 3) + "})").c_str()) ; } else sCell.append( "-, -, {-, -, -})") ; for ( int j = sCell.length() ; j < nSpace ; ++ j) sCell.append( " ") ; sRow.append( sCell) ; } LOG_ERROR( GetEMkLogger(), sRow.c_str()) ; } } //---------------------------------------------------------------------------- void Drilling::PrintDescent( const MHDrill& myMHDescent) { LOG_ERROR( GetEMkLogger(), ( " --- Tool main sull'uscita " + ToString( myMHDescent.currTool.pTool->m_nExit) + " per il foro " + ToString( m_vId[myMHDescent.nHoleInd].nId)).c_str()) ; string sActiveTool = "" ; for ( int _a = 0 ; _a < int( myMHDescent.vActiveExit.size()) ; ++ _a) sActiveTool.append( "->" + ToString( myMHDescent.vActiveExit[_a])) ; LOG_ERROR( GetEMkLogger(), ( " Uscite attive : " + sActiveTool).c_str()) ; string s_vtAux = "{" ; s_vtAux.append( ToString( myMHDescent.vtAux.x) + ", ") ; s_vtAux.append( ToString( myMHDescent.vtAux.y) + ", ") ; s_vtAux.append( ToString( myMHDescent.vtAux.z) + "}") ; LOG_ERROR( GetEMkLogger(), ( " vtAux : " + s_vtAux).c_str()) ; }