Files
EgtMachKernel/Drilling.cpp
T
Riccardo Elitropi e1ef39ad72 EgtMachKernel :
- in MachConst aggiunte Key per lettura da file .ini della macchina per Drilling in doppio in parallelo e per calcolo Feeds in PocketingNT
- in Drilling migliorate le considerazioni per lavorazioni in Doppio con possibilità di movimenti in parallelo per gli utensili
- in Preview Utensile corretta la posizione del secondo Tool in caso di lavorazioni in doppio con utensili di lunghezza differente
- in Machining aggiunta la funzione per calcolare il piano di Mirroring
- in PocketingNT aggiunta la lettura dal file .ini della macchina per calcolo delle Feeds.
2026-04-22 13:16:17 +02:00

3896 lines
154 KiB
C++

//----------------------------------------------------------------------------
// 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 <Hole, Tool>
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<pair<int, int>> 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<pair<int, double>> 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<pair<bool, double>> 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<OrderTabDrill> 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<ICurveComposite> 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<ICurve> 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<ICurveArc> 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<ICurveComposite> 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<ICurveArc> 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 ;
// verifico se la macchina presenta nel file .ini l'abilitazione per le forature in doppio in parallelo
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()) ;
}