//---------------------------------------------------------------------------- // EgalTech 2016-2016 //---------------------------------------------------------------------------- // File : Operation.cpp Data : 29.04.16 Versione : 1.6p4 // Contenuto : Implementazione gestione base operazioni. // // // // Modifiche : 29.04.16 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "DllMain.h" #include "Operation.h" #include "OperationConst.h" #include "OperUserNotesConst.h" #include "MachMgr.h" #include "OutputConst.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkAngle.h" #include "/EgtDev/Include/EGkBBox1d.h" #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkCurveLine.h" #include "/EgtDev/Include/EGkCurveArc.h" #include "/EgtDev/Include/EGkArcSpecial.h" #include "/EgtDev/Include/EGkCurveComposite.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EGkIntersLineSurfTm.h" #include "/EgtDev/Include/EGkGeomDB.h" #include "/EgtDev/Include/EGkCDeBoxClosedSurfTm.h" #include "/EgtDev/Include/EGkCDeCylClosedSurfTm.h" #include "/EgtDev/Include/EGkCAvToolSurfTm.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EXeConst.h" #include "/EgtDev/Include/EgtNumUtils.h" #include "/EgtDev/Include/EgtPointerOwner.h" using namespace std ; //---------------------------------------------------------------------------- const double MAX_DIST_RAW = 200.0 ; const double MAX_ROBOT_G0_LEN = 101.0 ; //---------------------------------------------------------------------------- bool Operation::SetOwner( int nId, IGeomDB* pGDB) { m_nOwnerId = nId ; m_pGeomDB = pGDB ; return ( m_nOwnerId != GDB_ID_NULL && m_pGeomDB != nullptr) ; } //---------------------------------------------------------------------------- int Operation::GetOwner( void) const { return m_nOwnerId ; } //---------------------------------------------------------------------------- IGeomDB* Operation::GetGeomDB( void) const { return m_pGeomDB ; } //---------------------------------------------------------------------------- static bool IsActiveOperation( int nId, IGeomDB* pGDB) { int nMode ; return ( pGDB != nullptr && pGDB->GetCalcMode( nId, nMode) && nMode == GDB_MD_STD) ; } //---------------------------------------------------------------------------- bool Operation::Removing( int nParentId, int nNextId) { // acconsento alla rimozione anche se non inizializzato if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return true ; // imposto da verificare operazione attiva successiva int nActId = nNextId ; while ( nActId != GDB_ID_NULL && ! IsActiveOperation( nActId, m_pGeomDB)) nActId = m_pGeomDB->GetNextGroup( nActId) ; // se non trovata, imposto da verificare operazione attiva precedente if ( nActId == GDB_ID_NULL) { nActId = m_pGeomDB->GetPrevGroup( m_nOwnerId) ; while ( nActId != GDB_ID_NULL && ! IsActiveOperation( nActId, m_pGeomDB)) nActId = m_pGeomDB->GetPrevGroup( nActId) ; } // imposto stato operazione Operation* pOpe = GetOperation( m_pGeomDB->GetUserObj( nActId)) ; if ( pOpe != nullptr) pOpe->UpdateStatus( MCH_ST_TO_VERIFY) ; return true ; } //---------------------------------------------------------------------------- bool Operation::Relocate( int nOrigParentId, int nOrigNextId, int nRefId, int nSonBeforeAfter, bool bGlob) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // imposto da verificare operazione UpdateStatus( MCH_ST_TO_VERIFY) ; // imposto da verificare operazione attiva successiva in posizione originale while ( nOrigNextId != GDB_ID_NULL && ! IsActiveOperation( nOrigNextId, m_pGeomDB)) nOrigNextId = m_pGeomDB->GetNextGroup( nOrigNextId) ; Operation* pOpe = GetOperation( m_pGeomDB->GetUserObj( nOrigNextId)) ; if ( pOpe != nullptr) pOpe->UpdateStatus( MCH_ST_TO_VERIFY) ; return true ; } //---------------------------------------------------------------------------- bool Operation::Init( MachMgr* pMchMgr) { m_pMchMgr = pMchMgr ; if ( m_pMchMgr == nullptr) return false ; if ( m_pMchMgr->GetGeomDB() != m_pGeomDB) return false ; return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- Operation::Operation( void) : m_nOwnerId( GDB_ID_NULL), m_pGeomDB( nullptr), m_pMchMgr( nullptr), m_nPhase( 1), m_nPathId( GDB_ID_NULL), m_bCurr( false), m_ptCurr(), m_vtTool(), m_vtCorr(), m_vtAux(), m_dFeed( 0), m_nFlag( 0), m_nFlag2( 0), m_nIndex( 0) { } //---------------------------------------------------------------------------- bool Operation::IsAtLeastOnePathOk( void) const { int nCnt ; int nEmpty ; if ( ! GetToolpathsStatus( nCnt, nEmpty)) return false ; return ( nCnt > 0 && nEmpty < nCnt) ; } //---------------------------------------------------------------------------- bool Operation::AreAllPathsOk( void) const { int nCnt ; int nEmpty ; if ( ! GetToolpathsStatus( nCnt, nEmpty)) return false ; return ( nCnt > 0 && nEmpty == 0) ; } //---------------------------------------------------------------------------- bool Operation::GetToolpathsStatus( int& nCnt, int& nEmpty) const { nCnt = 0 ; nEmpty = 0 ; if ( m_pGeomDB == nullptr) return false ; int nClPath = m_pGeomDB->GetFirstGroupInGroup( m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_CL)) ; while ( nClPath != GDB_ID_NULL) { ++ nCnt ; if ( m_pGeomDB->GetFirstInGroup( nClPath) == GDB_ID_NULL) ++ nEmpty ; nClPath = m_pGeomDB->GetNextGroup( nClPath) ; } return true ; } //---------------------------------------------------------------------------- int Operation::GetFirstFullToolpath( bool bMain) const { if ( m_pGeomDB == nullptr) return GDB_ID_NULL ; int nClPath = m_pGeomDB->GetFirstGroupInGroup( m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, ( bMain ? MCH_CL : MCH_DBL))) ; while ( nClPath != GDB_ID_NULL && m_pGeomDB->GetFirstInGroup( nClPath) == GDB_ID_NULL) nClPath = m_pGeomDB->GetNextGroup( nClPath) ; return nClPath ; } //---------------------------------------------------------------------------- int Operation::GetNextFullToolpath( int nClPathId, bool bMain) const { string sName ; do { nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ; } while ( nClPathId != GDB_ID_NULL && m_pGeomDB->GetFirstInGroup( nClPathId) == GDB_ID_NULL && m_pGeomDB->GetName( nClPathId, sName) && EqualNoCase( sName, bMain ? MCH_DBL : MCH_CL)) ; return nClPathId ; } //---------------------------------------------------------------------------- int Operation::GetLastFullToolpath( bool bMain) const { if ( m_pGeomDB == nullptr) return GDB_ID_NULL ; int nClPath = m_pGeomDB->GetLastGroupInGroup( m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, ( bMain ? MCH_CL : MCH_DBL))) ; while ( nClPath != GDB_ID_NULL && m_pGeomDB->GetFirstInGroup( nClPath) == GDB_ID_NULL) nClPath = m_pGeomDB->GetPrevGroup( nClPath) ; return nClPath ; } //---------------------------------------------------------------------------- int Operation::GetPrevFullToolpath( int nClPathId, bool bMain) const { string sName ; do { nClPathId = m_pGeomDB->GetPrevGroup( nClPathId) ; } while ( nClPathId != GDB_ID_NULL && m_pGeomDB->GetFirstInGroup( nClPathId) == GDB_ID_NULL && m_pGeomDB->GetName( nClPathId, sName) && EqualNoCase( sName, bMain ? MCH_DBL : MCH_CL)) ; return nClPathId ; } //---------------------------------------------------------------------------- bool Operation::UpdateFollowingOperationsStatus(int nModif) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // modifico lo stato delle operazioni attive successive int nOpeId = m_pMchMgr->GetNextActiveOperation( m_nOwnerId) ; while ( nOpeId != GDB_ID_NULL) { Operation* pOpe = GetOperation( m_pGeomDB->GetUserObj( nOpeId)) ; if ( pOpe != nullptr) pOpe->UpdateStatus( nModif) ; nOpeId = m_pMchMgr->GetNextActiveOperation( nOpeId) ; } return true ; } //---------------------------------------------------------------------------- string Operation::GetName( void) const { string sName ; if ( m_pGeomDB == nullptr || ! m_pGeomDB->GetName( m_nOwnerId, sName)) sName = "___" ; return sName ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double& dElev) const { Vector3d vtNorm ; INTVECTOR vRawStmId ; return GetElevation( nPhase, ptP, vtDir, dElev, vtNorm, vRawStmId) ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double& dElev, Vector3d& vtNorm) const { INTVECTOR vRawStmId ; return GetElevation( nPhase, ptP, vtDir, dElev, vtNorm, vRawStmId) ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double& dElev, INTVECTOR& vRawStmId) const { Vector3d vtNorm ; return GetElevation( nPhase, ptP, vtDir, dElev, vtNorm, vRawStmId) ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double& dElev, Vector3d& vtNorm, INTVECTOR& vRawStmId) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo elevazione dElev = INFINITO ; // inizializzo la normale vtNorm = V_NULL ; // ciclo sui grezzi bool bFound = false ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // intersezione del raggio dal punto alla trimesh del grezzo const double RAY_LEN = 100000 ; int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nStmId)) ; if ( pStm != nullptr) { bFound = true ; // indice del triangolo intersecato int nTria = SVT_NULL ; // recupero il riferimento della trimesh Frame3d frStm ; m_pGeomDB->GetGlobFrame( nStmId, frStm) ; // porto il raggio in questo riferimento Point3d ptPL = ptP ; ptPL.ToLoc( frStm) ; Vector3d vtDirL = vtDir ; vtDirL.ToLoc( frStm) ; ILSIVECTOR vInfo ; if ( IntersLineSurfTm( ptPL, vtDirL, RAY_LEN, *pStm, vInfo)) { for ( int i = 0 ; i < int( vInfo.size()) ; ++ i) { const IntLinStmInfo& Info = vInfo[i] ; // se tratto di intersezione coincidente, considero la posizione più lontana, ovvero 2 if ( Info.nILTT == ILTT_SEGM || Info.nILTT == ILTT_SEGM_ON_EDGE) { // se prosegue un tratto precedente, non devo controllare sia minimo if ( abs( dElev - Info.dU) < EPS_SMALL) { dElev = Info.dU2 ; nTria = Info.nT ; } else if ( Info.dU2 < dElev) { dElev = Info.dU2 ; nTria = Info.nT ; } } // se altrimenti intersezione puntuale, verifico che esca (coseno >= 0) else if ( Info.dCosDN > - COS_ORTO_ANG_ZERO) { if ( Info.dU < dElev) { dElev = Info.dU ; nTria = Info.nT ; } } // se altrimenti è la prima ed entra else if ( i == 0 && Info.dCosDN < - COS_ORTO_ANG_ZERO) { // se una sola o distante salto tutto if ( vInfo.size() == 1 || Info.dU > ( dElev > INFINITO - 1 ? 0 : dElev) + MAX_DIST_RAW) break ; // altrimenti reset else dElev = INFINITO ; } } } // se c'è triangolo di intersezione, ne recupero la normale e salvo Id del grezzo if ( nTria != SVT_NULL) { Triangle3d Tria ; if ( pStm->GetTriangle( nTria, Tria)) vtNorm = Tria.GetN() ; if ( find( vRawStmId.begin(), vRawStmId.end(), nStmId) == vRawStmId.end()) vRawStmId.emplace_back( nStmId) ; } } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se non trovate intersezioni, elevazione nulla if ( dElev > INFINITO - 1) dElev = 0 ; return bFound ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP1, const Point3d& ptP2, const Vector3d& vtDir, double& dElev) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo elevazione dElev = INFINITO ; // ciclo sui grezzi bool bFound = false ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // intersezione del raggio dal punto alla trimesh del grezzo const double RAY_LEN = 10000 ; int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nStmId)) ; if ( pStm != nullptr) { bFound = true ; // recupero il riferimento della trimesh Frame3d frStm ; m_pGeomDB->GetGlobFrame( nStmId, frStm) ; // porto il segmento e la direzione in questo riferimento Point3d ptP1L = ptP1 ; ptP1L.ToLoc( frStm) ; Point3d ptP2L = ptP2 ; ptP2L.ToLoc( frStm) ; Vector3d vtDirL = vtDir ; vtDirL.ToLoc( frStm) ; ILSIVECTOR vInfo ; // inizializzo elevazione della superficie double dElevS = 0 ; // faccio test con un insieme di punti ( !!! sostituire con intersezione tra rettangolo e trimesh !!!) const double STEP = 50 ; int nStep = max( (int) ceil( ApproxDist( ptP1L, ptP2L) / STEP), 2) ; for ( int i = 0 ; i <= nStep ; ++ i) { // calcolo punto di test double dFraz = i / (double) nStep ; Point3d ptPL = Media( ptP1L, ptP2L, dFraz) ; // inizializzo elevazione del punto double dElevP = INFINITO ; // calcolo elevazione sul punto if ( IntersLineSurfTm( ptPL, vtDirL, RAY_LEN, *pStm, vInfo)) { for ( int i = 0 ; i < int( vInfo.size()) ; ++ i) { const IntLinStmInfo& Info = vInfo[i] ; // se tratto di intersezione coincidente, considero la posizione più lontana, ovvero 2 if ( Info.nILTT == ILTT_SEGM || Info.nILTT == ILTT_SEGM_ON_EDGE) { // se prosegue un tratto precedente, non devo controllare sia minimo if ( abs( dElevP - Info.dU) < EPS_SMALL) dElevP = Info.dU2 ; else dElevP = min( dElevP, Info.dU2) ; } // se altrimenti intersezione puntuale, verifico che esca (coseno >= 0) else if ( Info.dCosDN > - COS_ORTO_ANG_ZERO) { dElevP = min( dElevP, Info.dU) ; } // se altrimenti è la prima, distante ed entra, salto tutto else if ( i == 0 && Info.dU > MAX_DIST_RAW && Info.dCosDN < - COS_ORTO_ANG_ZERO) break ; } } // se elevazione calcolata, aggiorno quella della superficie if ( dElevP < INFINITO - 1) dElevS = max( dElevS, dElevP) ; } // aggiorno elevazione complessiva if ( dElevS > 0) dElev = min( dElev, dElevS) ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se non trovate intersezioni, elevazione nulla if ( dElev > INFINITO - 1) dElev = 0 ; return bFound ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP1, const Point3d& ptP2, const Point3d& ptP3, const Vector3d& vtDir, double& dElev) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // approssimo l'arco con due linee double dElev1, dElev2 ; if ( ! GetElevation( nPhase, ptP1, ptP2, vtDir, dElev1)) return false ; if ( ! GetElevation( nPhase, ptP2, ptP3, vtDir, dElev2)) return false ; dElev = max( dElev1, dElev2) ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtTool, double dRad, const Vector3d& vtDir, double& dElev) const { const double STD_TOOL_LEN = 100 ; return GetElevation( nPhase, ptP, vtTool, dRad, STD_TOOL_LEN, 0, vtDir, dElev) ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtTool, double dRad, double dLen, const Vector3d& vtDir, double& dElev) const { return GetElevation( nPhase, ptP, vtTool, dRad, dLen, 0, vtDir, dElev) ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtTool, double dRad, double dLen, double dSafeDist, const Vector3d& vtDir, double& dElev) const { // risultato di default dElev = 0 ; // verifico oggetti base if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // aggiustamenti per distanza di sicurezza Point3d ptQ = ptP ; if ( dSafeDist > EPS_SMALL) { ptQ += -vtTool * dSafeDist ; dRad += dSafeDist ; dLen += 2 * dSafeDist ; } // inizializzo elevazioni per ogni grezzo INTDBLVECTOR vRawElev ; // ciclo sui grezzi della fase int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // recupero la trimesh del grezzo int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nStmId)) ; if ( pStm != nullptr) { // recupero il riferimento della trimesh Frame3d frStm ; m_pGeomDB->GetGlobFrame( nStmId, frStm) ; // porto posizione e direzioni in questo riferimento Point3d ptQL = GetToLoc( ptQ, frStm) ; Vector3d vtToolL = GetToLoc( vtTool, frStm) ; Vector3d vtDirL = GetToLoc( vtDir, frStm) ; // determino quanto allontanarsi PtrOwner pCAvTlStm( CreateCAvToolSurfTm()) ; if ( IsNull( pCAvTlStm)) return false ; pCAvTlStm->SetStdTool( dLen, dRad, dSafeDist) ; pCAvTlStm->SetSurfTm( *pStm) ; double dDist = 0 ; // non è ammesso un angolo oltre 90deg tra direzione utensile e vettore movimento if ( vtToolL * vtDirL < 0) { // il tip utensile è qui il naso mandrino per Collision Avoid if ( ! pCAvTlStm->TestPosition( ptQL, -vtToolL, vtDirL, dDist)) return false ; } else { // per Collision Avoid il punto di riferimento non è il tip dell'utensile ma il naso mandrino if ( ! pCAvTlStm->TestPosition( ptQL + dLen * vtToolL, vtToolL, vtDirL, dDist)) return false ; } if ( dDist > EPS_SMALL) vRawElev.emplace_back( nStmId, dDist) ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se trovate elevazioni if ( ! vRawElev.empty()) { // ordino il vettore secondo l'elevazione crescente sort( vRawElev.begin(), vRawElev.end(), []( const INTDBL& a, const INTDBL& b) { return a.second < b.second ; }) ; // box dell'utensile nella posizione iniziale BBox3d b3Tool ; b3Tool.Add( ptQ) ; b3Tool.Add( ptQ + dLen * vtTool) ; if ( vtTool.IsX()) b3Tool.Expand( 0, dRad, dRad) ; else if ( vtTool.IsY()) b3Tool.Expand( dRad, 0, dRad) ; else if ( vtTool.IsZ()) b3Tool.Expand( dRad, dRad, 0) ; else { double dExpandX = dRad * sqrt( 1 - vtTool.x * vtTool.x) ; double dExpandY = dRad * sqrt( 1 - vtTool.y * vtTool.y) ; double dExpandZ = dRad * sqrt( 1 - vtTool.z * vtTool.z) ; b3Tool.Expand( dExpandX, dExpandY, dExpandZ) ; } b3Tool.Expand( MAX_DIST_RAW) ; // verifico la reale interferenza dell'utensile con i diversi grezzi for ( int i = 0 ; i < int( vRawElev.size()) ; ++ i) { // box del grezzo BBox3d b3Raw ; m_pGeomDB->GetGlobalBBox( vRawElev[i].first, b3Raw) ; // confronto con il box dell'utensile nella posizione precedente BBox3d b3CurrTool = b3Tool ; b3CurrTool.Translate( dElev * vtDir) ; if ( b3Raw.Overlaps( b3CurrTool)) dElev = vRawElev[i].second ; } } return true ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtTool, double dRad, double dLen, const Vector3d& vtDir, double& dElev) const { // risultato di default bool bOk = false ; dElev = 0 ; // faccio test sui punti dall'inizio alla fine const double STEP = 50 ; int nStep = max( (int) ceil( ApproxDist( ptS, ptE) / STEP), 2) ; for ( int i = 0 ; i <= nStep ; ++ i) { // calcolo punto di test double dFraz = i / (double) nStep ; Point3d ptP = Media( ptS, ptE, dFraz) ; // eseguo il calcolo double dCurrElev ; if ( GetElevation( nPhase, ptP, vtTool, dRad, dLen, vtDir, dCurrElev)) { bOk = true ; dElev = max( dElev, dCurrElev) ; } } return bOk ; } //---------------------------------------------------------------------------- bool Operation::GetAhPointUnderRaw( const Point3d& ptP, const Vector3d& vtTool, double dToolRad, double dToolRadForElev, double dToolLen, bool bIsSaw, double dSafeZ, const Vector3d& vtDir, double& dElev) const { // punto a metà lunghezza utensile Point3d ptQ = ptP + vtTool * dToolLen / 2 ; // box di ingombro dell'utensile BBox3d b3Tool ; b3Tool.Set( ptP) ; b3Tool.Add( ptQ) ; double dExpandX = sqrt( 1 - vtTool.x * vtTool.x) * dToolRadForElev ; double dExpandY = sqrt( 1 - vtTool.y * vtTool.y) * dToolRadForElev ; double dExpandZ = sqrt( 1 - vtTool.z * vtTool.z) * dToolRadForElev ; b3Tool.Expand( dExpandX, dExpandY, dExpandZ) ; // extra ingombro reale in Z double dExtraZ = sqrt( 1 - vtTool.z * vtTool.z) * dToolRad ; // direzione di fuga per l'elevazione Vector3d vtMyDir = vtDir ; if ( bIsSaw || vtMyDir.z < - EPS_SMALL) vtMyDir.z = 0 ; vtMyDir.Normalize() ; // determino la posizione del punto rispetto al grezzo // ciclo sui grezzi int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // se il grezzo compare nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) { int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; BBox3d b3Raw ; m_pGeomDB->GetGlobalBBox( nStmId, b3Raw) ; b3Raw.Expand( 0.5 * dSafeZ, 0.5 * dSafeZ, 0) ; if ( ! b3Raw.IsEmpty() && b3Raw.OverlapsXY( b3Tool) && ptP.z + dExtraZ < b3Raw.GetMin().z - 10 * EPS_SMALL) { double dRawCentZ = ( b3Raw.GetMin().z + b3Raw.GetMax().z) / 2 ; double dRawDimZ = b3Raw.GetMax().z - b3Raw.GetMin().z ; double dToolDimZ = b3Tool.GetMax().z - b3Tool.GetMin().z ; // determino elevazione del punto rispetto al grezzo a metà altezza Point3d ptTest( ptP.x, ptP.y, dRawCentZ) ; double dNewElev ; if ( GetElevation( m_nPhase, ptTest, vtTool, dToolRadForElev, vtMyDir, dNewElev)) dElev = max( dElev, dNewElev) ; // se direzione di fuga quasi orizzontale (max 30 deg) e ingombro utensile rilevante rispetto ad altezza grezzo if ( vtMyDir.z < 0.5 && dToolDimZ > dRawDimZ / 2) { // determino elevazione del punto rispetto al grezzo a metà altezza più metà ingombro utensile ptTest = Point3d( ptP.x, ptP.y, dRawCentZ + dToolDimZ / 2) ; if ( GetElevation( m_nPhase, ptTest, vtTool, dToolRadForElev, vtMyDir, dNewElev)) dElev = max( dElev, dNewElev) ; // determino elevazione del punto rispetto al grezzo a metà altezza meno metà ingombro utensile ptTest = Point3d( ptP.x, ptP.y, dRawCentZ - dToolDimZ / 2) ; if ( GetElevation( m_nPhase, ptTest, vtTool, dToolRadForElev, vtMyDir, dNewElev)) dElev = max( dElev, dNewElev) ; } return true ; } } nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return false ; } //---------------------------------------------------------------------------- bool Operation::GetUhPointAboveRaw( const Point3d& ptP, const Vector3d& vtTool, double dToolRad, double dToolRadForElev, double dToolLen, bool bIsSaw, double dSafeZ, const Vector3d& vtDir, double& dElev) const { // punto a metà lunghezza utensile Point3d ptQ = ptP + vtTool * dToolLen / 2 ; // box di ingombro dell'utensile BBox3d b3Tool ; b3Tool.Set( ptP) ; b3Tool.Add( ptQ) ; double dExpandX = sqrt( 1 - vtTool.x * vtTool.x) * dToolRadForElev ; double dExpandY = sqrt( 1 - vtTool.y * vtTool.y) * dToolRadForElev ; double dExpandZ = sqrt( 1 - vtTool.z * vtTool.z) * dToolRadForElev ; b3Tool.Expand( dExpandX, dExpandY, dExpandZ) ; // extra ingombro reale in Z double dExtraZ = sqrt( 1 - vtTool.z * vtTool.z) * dToolRad ; // direzione di fuga per l'elevazione Vector3d vtMyDir = vtDir ; if ( bIsSaw || vtMyDir.z > EPS_SMALL) vtMyDir.z = 0 ; vtMyDir.Normalize() ; // determino la posizione del punto rispetto al grezzo // ciclo sui grezzi int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // se il grezzo compare nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) { int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; BBox3d b3Raw ; m_pGeomDB->GetGlobalBBox( nStmId, b3Raw) ; b3Raw.Expand( 0.5 * dSafeZ, 0.5 * dSafeZ, 0) ; if ( ! b3Raw.IsEmpty() && b3Raw.OverlapsXY( b3Tool) && ptP.z - dExtraZ > b3Raw.GetMax().z + 10 * EPS_SMALL) { double dRawCentZ = ( b3Raw.GetMin().z + b3Raw.GetMax().z) / 2 ; double dRawDimZ = b3Raw.GetMax().z - b3Raw.GetMin().z ; double dToolDimZ = b3Tool.GetMax().z - b3Tool.GetMin().z ; // determino elevazione del punto rispetto al grezzo a metà altezza Point3d ptTest( ptP.x, ptP.y, dRawCentZ) ; double dNewElev ; if ( GetElevation( m_nPhase, ptTest, vtTool, dToolRadForElev, vtMyDir, dNewElev)) dElev = max( dElev, dNewElev) ; // se direzione di fuga quasi orizzontale (max 30 deg) e ingombro utensile rilevante rispetto ad altezza grezzo if ( vtMyDir.z > -0.5 && dToolDimZ > dRawDimZ / 2) { // determino elevazione del punto rispetto al grezzo a metà altezza più metà ingombro utensile ptTest = Point3d( ptP.x, ptP.y, dRawCentZ + dToolDimZ / 2) ; if ( GetElevation( m_nPhase, ptTest, vtTool, dToolRadForElev, vtMyDir, dNewElev)) dElev = max( dElev, dNewElev) ; // determino elevazione del punto rispetto al grezzo a metà altezza meno metà ingombro utensile ptTest = Point3d( ptP.x, ptP.y, dRawCentZ - dToolDimZ / 2) ; if ( GetElevation( m_nPhase, ptTest, vtTool, dToolRadForElev, vtMyDir, dNewElev)) dElev = max( dElev, dNewElev) ; } return true ; } } nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return false ; } //---------------------------------------------------------------------------- bool Operation::GetDistanceFromRawSide( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double& dDist) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero il grezzo più vicino al punto (entro 100 mm) const double EXPAND_LEN = 100 ; BBox3d b3Pnt( ptP) ; b3Pnt.Expand( EXPAND_LEN, EXPAND_LEN, 0) ; double dMinDist = INFINITO ; int nRawId = GDB_ID_NULL ; int nCurrRawId = m_pMchMgr->GetFirstRawPart() ; while ( nCurrRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nCurrRawId, nPhase)) { // pre-filtro con il box BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nCurrRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw) && b3Pnt.OverlapsXY( b3Raw)) { // porto il punto nel riferimento del grezzo (a Z=0 perchè ivi è il contorno) Frame3d frRaw ; m_pGeomDB->GetGroupGlobFrame( nCurrRawId, frRaw) ; Point3d ptPL = ptP ; ptPL.ToLoc( frRaw) ; ptPL.z = 0 ; // verifica con il contorno int nOutCrvId = m_pGeomDB->GetFirstNameInGroup( nCurrRawId, MACH_RAW_OUTLINE) ; const ICurve* pCurve = GetCurve( m_pGeomDB->GetGeoObj( nOutCrvId)) ; if ( pCurve != nullptr) { int nSide ; double dCurrDist ; DistPointCurve distPC( ptPL, *pCurve) ; if ( distPC.GetDist( dCurrDist)) { if ( dCurrDist < 100 * EPS_SMALL || (distPC.GetSideAtMinDistPoint( 0, Z_AX, nSide) && nSide != MDS_RIGHT)) { nRawId = nCurrRawId ; break ; } else if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nRawId = nCurrRawId ; } } } } } // passo al grezzo successivo nCurrRawId = m_pMchMgr->GetNextRawPart( nCurrRawId) ; } // se il punto non è interno ad alcun grezzo, distanza nulla if ( nRawId == GDB_ID_NULL) { dDist = 0 ; return true ; } // recupero il contorno del grezzo int nOutId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_OUTLINE) ; ICurve* pOut = ::GetCurve( m_pGeomDB->GetGeoObj( nOutId)) ; if ( pOut == nullptr) return false ; // recupero il riferimento del contorno e verifico che abbia Z verticale Frame3d frStm ; if ( ! m_pGeomDB->GetGlobFrame( nOutId, frStm) || ( frStm.GetZType() != Frame3d::TOP && frStm.GetZType() != Frame3d::BOTTOM)) return false ; // interseco il raggio di test (portato nel riferimento del contorno) con il contorno del grezzo const double RAY_LEN = 10000 ; const double EXTRA_LEN = EXPAND_LEN ; PtrOwner pRay( CreateCurveLine()) ; if ( IsNull( pRay) || ! pRay->SetPVL( ptP - vtDir * EXTRA_LEN, vtDir, RAY_LEN)) return false ; pRay->ToLoc( frStm) ; IntersCurveCurve intCC( *pRay, *pOut) ; int nInters = intCC.GetIntersCount() ; IntCrvCrvInfo aInfo ; if ( nInters > 0 && intCC.GetIntCrvCrvInfo( nInters - 1, aInfo)) { if ( ! aInfo.bOverlap) dDist = aInfo.IciA[0].dU * RAY_LEN - EXTRA_LEN ; else dDist = aInfo.IciA[1].dU * RAY_LEN - EXTRA_LEN ; } else dDist = 0 ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetAggrBottDistanceFromRawSide( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double dEncSafeH, double& dDist) const { double dDistB1, dDistB2, dDistB3 ; Vector3d vtOffs = dEncSafeH * GetRotate( vtDir, Z_AX, 90) ; if ( ! GetDistanceFromRawSide( m_nPhase, ptP, vtDir, dDistB1)) dDistB1 = 0 ; if ( ! GetDistanceFromRawSide( m_nPhase, ptP + vtOffs, vtDir, dDistB2)) dDistB2 = 0 ; if ( ! GetDistanceFromRawSide( m_nPhase, ptP - vtOffs, vtDir, dDistB3)) dDistB3 = 0 ; dDist = max( { dDistB1, dDistB2, dDistB3}) ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetMinDistanceFromRawSide( int nPhase, const Point3d& ptP, double dExpand, double& dDist, Vector3d& vtDir) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero il grezzo in cui è incluso il punto BBox3d b3Pnt( ptP) ; double dTotExpand = dExpand + 10 ; b3Pnt.Expand( dTotExpand, dTotExpand, 0) ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // pre-filtro con il box BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw) && b3Pnt.OverlapsXY( b3Raw)) { // porto il punto nel riferimento del grezzo (a Z=0 perchè ivi è il contorno) Frame3d frRaw ; m_pGeomDB->GetGroupGlobFrame( nRawId, frRaw) ; Point3d ptPL = ptP ; ptPL.ToLoc( frRaw) ; ptPL.z = 0 ; // verifica con il contorno int nOutCrvId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_OUTLINE) ; const ICurve* pCurve = GetCurve( m_pGeomDB->GetGeoObj( nOutCrvId)) ; if ( pCurve != nullptr) { int nSide ; DistPointCurve distPC( ptPL, *pCurve) ; // se punto praticamente sul bordo del grezzo, prendo la normale esterna a questo bordo if ( distPC.GetDist( dDist) && dDist < 100 * EPS_SMALL) { double dU ; int nFlag ; Point3d ptML ; Vector3d vtTgL ; if ( distPC.GetParamAtMinDistPoint( 0, dU, nFlag) && pCurve->GetPointTang( dU, ICurve::FROM_MINUS, ptML, vtTgL)) { vtDir = vtTgL ; vtDir.Rotate( Z_AX, 0, -1) ; vtDir.ToGlob( frRaw) ; vtDir.Normalize() ; break ; } } // altrimenti se punto interno, prendo la direzione dal punto al bordo else if ( distPC.GetSideAtMinDistPoint( 0, Z_AX, nSide) && nSide != MDS_RIGHT) { int nFlag ; Point3d ptML ; if ( distPC.GetMinDistPoint( 0, ptML, nFlag)) { vtDir = ptML - ptPL ; vtDir.ToGlob( frRaw) ; vtDir.z = 0 ; vtDir.Normalize() ; break ; } } // altrimenti punto esterno ma entro espansione (recupero la normale verso l'esterno) else if ( dDist < dTotExpand) { int nFlag ; Point3d ptML ; if ( distPC.GetMinDistPoint( 0, ptML, nFlag)) { dDist = - dDist ; vtDir = ptPL - ptML ; vtDir.ToGlob( frRaw) ; vtDir.z = 0 ; vtDir.Normalize() ; break ; } } } } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se il punto non è interno ad alcun grezzo, distanza nulla if ( nRawId == GDB_ID_NULL) dDist = 0 ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetMinDistanceFromRawSide( int nPhase, const Point3d& ptP, double dExpand, const Vector3d& vtMainDir, double dCosMaxDelta, double& dDist, Vector3d& vtDir) const { // calcolo senza limiti sulla direzione if ( ! GetMinDistanceFromRawSide( nPhase, ptP, dExpand, dDist, vtDir)) return false ; // se esterno, non previsti limiti o direzione nei limiti, esco if ( dDist < EPS_SMALL || vtMainDir.IsSmall() || dCosMaxDelta <= -1 || ( vtMainDir * vtDir) > dCosMaxDelta) return true ; // limito la direzione Vector3d vtPar = vtMainDir * dCosMaxDelta ; Vector3d vtOrt = OrthoCompo( vtDir, vtMainDir) ; double dSqOrt = 1 - dCosMaxDelta * dCosMaxDelta ; if ( vtOrt.Normalize() && dSqOrt > 0) vtDir = vtPar + vtOrt * sqrt( dSqOrt) ; else vtDir = vtMainDir ; // calcolo con direzione imposta return GetDistanceFromRawSide( nPhase, ptP, vtDir, dDist) ; } //---------------------------------------------------------------------------- bool Operation::GetDistanceFromRawBottom( int nPhase, int nPathId, double dToler, double& dRbDist, double& dAllRbDist) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero distanza da fondo dei grezzi interessati o no dal percorso dRbDist = 0 ; dAllRbDist = 0 ; BBox3d b3Compo ; if ( ! m_pGeomDB->GetGlobalBBox( nPathId, b3Compo)) return false ; b3Compo.Expand( dToler, dToler, 0) ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw)) { double dDist = b3Compo.GetMax().z - b3Raw.GetMin().z ; if ( b3Compo.OverlapsXY( b3Raw) && dDist > dRbDist) dRbDist = dDist ; if ( dDist > dAllRbDist) dAllRbDist = dDist ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::GetRawGlobBox( int nPhase, int nPathId, double dToler, BBox3d& b3Raw) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo box b3Raw.Reset() ; // ricerco grezzi interessati dal percorso BBox3d b3Compo ; if ( ! m_pGeomDB->GetGlobalBBox( nPathId, b3Compo)) return false ; return GetRawGlobBox( nPhase, b3Compo, dToler, b3Raw) ; } //---------------------------------------------------------------------------- bool Operation::GetRawGlobBox( int nPhase, const BBox3d& b3Test, double dToler, BBox3d& b3Raw) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo box b3Raw.Reset() ; // allargo box di test BBox3d b3Compo = b3Test ; b3Compo.Expand( dToler, dToler, 0) ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { BBox3d b3OneRaw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3OneRaw) && b3Compo.OverlapsXY( b3OneRaw)) { b3Raw.Add( b3OneRaw) ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::GetCurrRawsGlobBox( BBox3d& b3Raw) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo box b3Raw.Reset() ; // Ciclo sui grezzi attivi int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) { BBox3d b3OneRaw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3OneRaw)) { b3Raw.Add( b3OneRaw) ; } } nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- static Vector3d GetDirInFacePerpSide( int nFaceUse, const Vector3d& vtFaceUse, const Vector3d& vtN, ICurveComposite* pCrvCompo) { // determino la direzione di riferimento proiettata nella faccia Vector3d vtRef ; switch ( nFaceUse) { case FACE_DOWN : vtRef = Z_AX ; break ; case FACE_TOP : vtRef = -Z_AX ; break ; case FACE_FRONT : vtRef = Y_AX ; break ; case FACE_BACK : vtRef = -Y_AX ; break ; case FACE_LEFT : vtRef = X_AX ; break ; case FACE_RIGHT : vtRef = -X_AX ; break ; case FACE_VERSOR : vtRef = vtFaceUse ; break ; } vtRef -= vtRef * vtN * vtN ; if ( ! vtRef.Normalize()) vtRef = V_NULL ; // sostituisco questa direzione con quella perpendicolare alla curva più adatta double dCosMax = -1 ; Vector3d vtPerpMax ; const ICurve* pCrv = pCrvCompo->GetFirstCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetMidDir( vtDir) ; Vector3d vtPerp = vtN ^ vtDir ; double dCos = vtPerp * vtRef ; if ( dCos > dCosMax) { dCosMax = dCos ; vtPerpMax = vtPerp ; } pCrv = pCrvCompo->GetNextCurve() ; } // richiedo non più di 85deg di deviazione if ( dCosMax > 0.087) vtRef = vtPerpMax ; return vtRef ; } //---------------------------------------------------------------------------- bool Operation::AdjustCurveFromSurf( ICurveComposite* pCrvCompo, int nToolDir, int nFaceUse, const Vector3d& vtFaceUse, const INTVECTOR& vnEdgesFaceUse, double dToolThick, int nGrade) { // copia della curva originale PtrOwner pCopy( pCrvCompo->Clone()) ; // versore normale alla faccia (estrusione della curva) Vector3d vtN ; pCrvCompo->GetExtrusion( vtN) ; // direzione di riferimento Vector3d vtRef ; // direzioni lati precedente e successivo Vector3d vtDirPrev ; Vector3d vtDirNext ; // se richiesto contorno if ( nFaceUse == FACE_CONT) { // cerco l'estremo più alto e lo imposto come inizio double dU = 0 ; double dUmax = 0 ; double dZmax = - INFINITO ; Point3d ptP ; while ( pCrvCompo->GetPointD1D2( dU, ICurve::FROM_MINUS, ptP)) { if ( ptP.z > dZmax + EPS_SMALL) { dZmax = ptP.z ; dUmax = dU ; } dU += 1 ; } if ( dUmax != 0) pCrvCompo->ChangeStartPoint( dUmax) ; } // se altrimenti richiesta una parte del contorno else if ( nFaceUse == FACE_EDGES) { // verifico che il vettore degli indici dei lati non sia vuoto if ( vnEdgesFaceUse.empty()) return false ; // parametri di trim double dUStart = vnEdgesFaceUse.front() ; double dUEnd = vnEdgesFaceUse.back() + 1 ; // taglio le parti che non interessano if ( ! pCrvCompo->TrimStartEndAtParam( dUStart, dUEnd)) return false ; } // altrimenti else { // determino la direzione di riferimento vtRef = GetDirInFacePerpSide( nFaceUse, vtFaceUse, vtN, pCrvCompo) ; // la curva gira in senso antiorario attorno al contorno faccia vista dalla normale uscente // elimino i segmenti che hanno la direzione di riferimento a destra o quasi (16 deg o 31 deg o 46deg) nGrade = Clamp( nGrade, 1, 3) ; const double COS_ANG_MAX = ( nGrade == 1 ? cos( 16.01 * DEGTORAD) : ( nGrade == 2 ? cos( 31.01 * DEGTORAD) : cos( 46.01 * DEGTORAD))) ; // cerco primo elemento del contorno non valido e vi pongo inizio/fine della curva int i = 0 ; const ICurve* pCrv = pCrvCompo->GetFirstCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetMidDir( vtDir) ; if ( ( vtN ^ vtDir) * vtRef < COS_ANG_MAX) { vtDirPrev = vtDir ; vtDirNext = vtDir ; pCrvCompo->ChangeStartPoint( i) ; break ; } pCrv = pCrvCompo->GetNextCurve() ; ++ i ; } // a partire da questo elimino elementi non validi in avanti e indietro pCrv = pCrvCompo->GetFirstCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetMidDir( vtDir) ; if ( ( vtN ^ vtDir) * vtRef < COS_ANG_MAX) { delete( pCrvCompo->RemoveFirstOrLastCurve( false)) ; pCrv = pCrvCompo->GetFirstCurve() ; vtDirPrev = vtDir ; } else break ; } pCrv = pCrvCompo->GetLastCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetMidDir( vtDir) ; if ( ( vtN ^ vtDir) * vtRef < COS_ANG_MAX) { delete( pCrvCompo->RemoveFirstOrLastCurve( true)) ; pCrv = pCrvCompo->GetLastCurve() ; vtDirNext = vtDir ; } else break ; } // elimino le curve estreme molto corte ( 0.5 mm) const double REF_LEN = 10.0 ; double dLen ; if ( pCrvCompo->GetLength( dLen) && dLen > REF_LEN) { const double MIN_LEN = 0.5 ; double dCrvLen ; pCrv = pCrvCompo->GetFirstCurve() ; if ( pCrv != nullptr && pCrv->GetLength( dCrvLen) && dCrvLen < MIN_LEN) delete( pCrvCompo->RemoveFirstOrLastCurve( false)) ; pCrv = pCrvCompo->GetLastCurve() ; if ( pCrv != nullptr && pCrv->GetLength( dCrvLen) && dCrvLen < MIN_LEN) delete( pCrvCompo->RemoveFirstOrLastCurve( true)) ; } } // determino il versore estrusione (utensile) if ( nToolDir == TOOL_ORTHO) pCrvCompo->SetThickness( 0) ; else if ( nToolDir == TOOL_ORTUP) { if ( vtN.z < - EPS_SMALL) { pCrvCompo->Translate( vtN * dToolThick) ; pCrvCompo->SetExtrusion( - vtN) ; } pCrvCompo->SetThickness( 0) ; } else { // nToolDir == TOOL_PARAL o TOOL_PAR_SLANT // sistemo versore Vector3d vtExtr ; Vector3d vtTm ; pCrvCompo->GetMidDir( vtTm) ; Vector3d vtEm = vtN ^ vtTm ; if ( vtEm.Normalize()) vtExtr = vtEm ; Vector3d vtTs ; pCrvCompo->GetStartDir( vtTs) ; Vector3d vtEs = vtN ^ vtTs ; if ( vtEs.Normalize() && vtEs * vtRef > vtExtr * vtRef) vtExtr = vtEs ; Vector3d vtTe ; pCrvCompo->GetEndDir( vtTe) ; Vector3d vtEe = vtN ^ vtTe ; if ( vtEe.Normalize() && vtEe * vtRef > vtExtr * vtRef) vtExtr = vtEe ; if ( nToolDir == TOOL_PAR_SLANT) { Vector3d vtDirPN = -vtDirPrev + vtDirNext ; // se le direzioni prima e dopo non sono troppo diverse tra loro (15deg) e la loro media è normalizzabile if ( ( -vtDirPrev * vtDirNext) > cos( 15 * DEGTORAD) && vtDirPN.Normalize()) { if ( vtDirPN * vtExtr > 0) vtExtr = vtDirPN ; else vtExtr = -vtDirPN ; } } pCrvCompo->SetExtrusion( vtExtr) ; // elimino eventuali parti iniziali e/o finali dirette circa come il versore const double MAX_ALL_COS = cos( 25 * DEGTORAD) ; const ICurve* pCrv = pCrvCompo->GetFirstCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetStartDir( vtDir) ; if ( abs( vtDir * vtExtr) > MAX_ALL_COS) { ICurve* pErase = pCrvCompo->RemoveFirstOrLastCurve( false) ; delete( pErase) ; pCrv = pCrvCompo->GetFirstCurve() ; } else if ( pCrvCompo->IsClosed()) { pCrvCompo->ChangeStartPoint( 1) ; pCrv = pCrvCompo->GetFirstCurve() ; } else break ; } pCrv = pCrvCompo->GetLastCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetStartDir( vtDir) ; if ( abs( vtDir * vtExtr) > MAX_ALL_COS) { ICurve* pErase = pCrvCompo->RemoveFirstOrLastCurve( true) ; delete( pErase) ; pCrv = pCrvCompo->GetLastCurve() ; } else break ; } // determino lo spessore misurato lungo la direzione if ( ! IsNull( pCopy)) { Frame3d frExtr ; frExtr.Set( ORIG, vtExtr) ; frExtr.Invert() ; BBox3d b3Extr ; pCopy->GetBBox( frExtr, b3Extr) ; if ( ! b3Extr.IsEmpty()) { double dThick = b3Extr.GetMax().z - b3Extr.GetMin().z ; Point3d ptStart, ptEnd ; if ( nToolDir == TOOL_PAR_SLANT && pCrvCompo->IsALine( LIN_TOL_STD, ptStart, ptEnd)) { BBox3d b3Crv ; pCrvCompo->GetBBox( frExtr, b3Crv) ; if ( ! b3Crv.IsEmpty()) dThick -= b3Crv.GetMax().z - b3Crv.GetMin().z ; } pCrvCompo->SetThickness( dThick) ; } } } return true ; } //---------------------------------------------------------------------------- bool Operation::ApproxWithArcsIfUseful( ICurveComposite* pCompo, bool bCareTempProp) const { // recupero estrusione e spessore Vector3d vtExtr = Z_AX ; pCompo->GetExtrusion( vtExtr) ; double dThick = 0 ; pCompo->GetThickness( dThick) ; // verifico se ci sono tante linee corte int nSmallLineCnt = 0 ; const ICurve* pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { double dLen ; if ( pCrv->GetLength( dLen) && dLen < LIN_FEA_STD / 2) ++ nSmallLineCnt ; pCrv = pCompo->GetNextCurve() ; } if ( nSmallLineCnt < 10) return true ; // limito l'approssimazione alle curve piane Frame3d frRef ; frRef.Set( ORIG, vtExtr) ; Frame3d frInvRef = frRef ; frInvRef.Invert() ; BBox3d b3Crv ; if ( ! pCompo->GetBBox( frInvRef, b3Crv) || abs( b3Crv.GetMax().z - b3Crv.GetMin().z - dThick) > 100 * EPS_SMALL) return true ; // porto la curva nel suo piano perchè ApproxWithArcsEx funziona bene solo nel piano XY (all'uscita va ripristinato il rif. originale) pCompo->ToLoc( frRef) ; // se posso ignorare la proprietà temporanea if ( ! bCareTempProp) { // calcolo approssimazione con archi (se possibile) PolyArc PA ; if ( ! pCompo->ApproxWithArcsEx( LIN_TOL_MID, ANG_TOL_STD_DEG, LIN_FEA_STD, PA)) { pCompo->ToGlob( frRef) ; return true ; } // sostituisco gli archi alle curve originali pCompo->Clear() ; if ( ! pCompo->FromPolyArc( PA)) { pCompo->ToGlob( frRef) ; return false ; } } // altrimenti divido la curva in parti con la stessa proprietà e approssimo separatamente ciascuna parte else { // determino gli intervalli in cui dividere la curva INTINTVECTOR vInt ; int nPrevProp = - 1 ; int nCount = 0 ; const ICurve* pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { int nProp = pCrv->GetTempProp() ; if ( nPrevProp != -1 && nProp != nPrevProp) vInt.emplace_back( nCount, nPrevProp) ; // passo alla curva successiva nPrevProp = nProp ; ++ nCount ; pCrv = pCompo->GetNextCurve() ; } if ( nCount > 0) vInt.emplace_back( nCount, nPrevProp) ; // divido la curva negli intervalli e li interpolo ICURVEPOVECTOR vpCrvs ; vpCrvs.reserve( vInt.size()) ; for ( int i = 0 ; i < int( vInt.size()) ; ++ i) { // recupero l'intervallo vpCrvs.emplace_back( pCompo->Clone()) ; if ( IsNull( vpCrvs.back())) { pCompo->ToGlob( frRef) ; return false ; } vpCrvs.back()->TrimEndAtParam( vInt[i].first) ; if ( i > 0) vpCrvs.back()->TrimStartAtParam( vInt[i-1].first) ; // calcolo approssimazione con archi (se possibile) PolyArc PA ; if ( vpCrvs.back()->ApproxWithArcsEx( LIN_TOL_MID, ANG_TOL_STD_DEG, LIN_FEA_STD, PA)) { // sostituisco gli archi alle curve originali PtrOwner pCC( CreateCurveComposite()) ; if ( IsNull( pCC) || ! pCC->FromPolyArc( PA)) { pCompo->ToGlob( frRef) ; return false ; } // assegno la proprietà originale ad ogni curva semplice della composita for ( int j = 0 ; j < pCC->GetCurveCount() ; ++ j) pCC->SetCurveTempProp( j, vInt[i].second) ; // reinserisco nel vettore di curve vpCrvs.back().Set( pCC) ; } } // riassemblo la curva e assegno le opportune proprietà pCompo->Clear() ; for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { pCompo->AddCurve( Release( vpCrvs[i])) ; } } // riporto in globale pCompo->ToGlob( frRef) ; // riassegno estrusione e spessore pCompo->SetExtrusion( vtExtr) ; pCompo->SetThickness( dThick) ; return true ; } //---------------------------------------------------------------------------- bool Operation::ApproxWithLines( ICurveComposite* pCompo, bool bFeed) const { // recupero estrusione e spessore Vector3d vtExtr = Z_AX ; pCompo->GetExtrusion( vtExtr) ; double dThick = 0 ; pCompo->GetThickness( dThick) ; // calcolo approssimazione lineare PolyLine PL ; double dLinTol = GetApproxLinTol() ; if ( ! pCompo->ApproxWithLines( dLinTol, ANG_TOL_MID_DEG, ICurve::APL_SPECIAL, PL)) return false ; // se prevista massima lunghezza double dMaxLen = GetMaxSplitLen( true, bFeed) ; if ( dMaxLen > 99 * EPS_SMALL) PL.AdjustForMaxSegmentLen( dMaxLen) ; // sostituisco le linee alle curve originali pCompo->Clear() ; pCompo->FromPolyLine( PL) ; // riassegno estrusione e spessore pCompo->SetExtrusion( vtExtr) ; pCompo->SetThickness( dThick) ; return true ; } //---------------------------------------------------------------------------- bool Operation::VerifyArcs( ICurveComposite* pCompo, double dMaxAngCen) const { // verifiche sull'ampiezza dell'angolo al centro degli eventuali archi int nMaxInd = pCompo->GetCurveCount() - 1 ; for ( int i = 0 ; i <= nMaxInd ; ) { // se arco con angolo al centro oltre il limite, lo divido a metà const ICurveArc* pArc = GetCurveArc( pCompo->GetCurve( i)) ; if ( pArc != nullptr && abs( pArc->GetAngCenter()) > dMaxAngCen) { pCompo->AddJoint( i + 0.5) ; ++ nMaxInd ; } else ++ i ; } return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::CalcAndSetBBox( int nClId) { if ( m_pGeomDB == nullptr) return false ; // assegno ingombri dei vari percorsi di lavorazione e della lavorazione nel suo complesso BBox3d b3AllMch ; int nClPathId = m_pGeomDB->GetFirstInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { BBox3d b3Grp ; for ( int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; nEntId != GDB_ID_NULL ; nEntId = m_pGeomDB->GetNext( nEntId)) { // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) continue ; // recupero il punto finale e lo uso per aggiornare l'ingombro Point3d ptP = pCamData->GetEndPoint() ; AdjustEndPointForAxesCalc( pCamData, ptP) ; b3Grp.Add( ptP) ; // se arco, determino anche alcuni punti intermedi if ( pCamData->IsArc()) { // dati dell'arco Point3d ptCen = pCamData->GetCenter() ; AdjustArcCenterForAxesCalc( pCamData, ptCen) ; double dAngCen = pCamData->GetAngCen() ; Vector3d vtN = pCamData->GetNormDir() ; double dDeltaN = pCamData->GetDeltaN() ; // ricavo i punti const double ANG_STEP = 4.9 ; int nStep = 1 + int( abs( dAngCen) / ANG_STEP) ; double dCoeff = -1. / nStep ; double dCosRot = cos( dCoeff * dAngCen * DEGTORAD) ; double dSinRot = sin( dCoeff * dAngCen * DEGTORAD) ; Point3d ptT = ptP ; for ( int i = 1 ; i < nStep ; ++ i) { ptT.Rotate( ptCen, vtN, dCosRot, dSinRot) ; ptT.Translate( vtN * ( dCoeff * dDeltaN)) ; b3Grp.Add( ptT) ; } } } if ( ! b3Grp.IsEmpty()) { m_pGeomDB->SetInfo( nClPathId, KEY_PMIN, b3Grp.GetMin()) ; m_pGeomDB->SetInfo( nClPathId, KEY_PMAX, b3Grp.GetMax()) ; b3AllMch.Add( b3Grp) ; } nClPathId = m_pGeomDB->GetNext( nClPathId) ; } if ( ! b3AllMch.IsEmpty()) { m_pGeomDB->SetInfo( nClId, KEY_MMIN, b3AllMch.GetMin()) ; m_pGeomDB->SetInfo( nClId, KEY_MMAX, b3AllMch.GetMax()) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::CalcAndSetAxesBBox( void) { if ( m_pGeomDB == nullptr || m_pMchMgr == nullptr) return false ; // recupero gruppo della geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // recupero numero totale assi correnti int nLinAxes = m_pMchMgr->GetCurrLinAxes() ; int nRotAxes = m_pMchMgr->GetCurrRotAxes() ; int nTotAxes = nLinAxes + nRotAxes ; // assegno estremi assi dei vari percorsi di lavorazione e della lavorazione nel suo complesso vector vMmAxAll( nTotAxes) ; int nClPathId = m_pGeomDB->GetFirstInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { vector vMmAxGrp( nTotAxes) ; for ( int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; nEntId != GDB_ID_NULL ; nEntId = m_pGeomDB->GetNext( nEntId)) { // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) continue ; // movimenti in rapido da ignorare if ( pCamData->GetMoveType() == 0) { // se posizione in home if ( pCamData->GetFlag() == 4) continue ; // se posizione in alto string sName ; if ( m_pGeomDB->GetName( nEntId, sName) && ( sName == MCH_CL_CLIMB || sName == MCH_CL_RISE)) continue ; } // recupero gli assi macchina e li uso per aggiornare l'ingombro DBLVECTOR AxesVal = pCamData->GetAxesVal() ; for ( int i = 0 ; i < nTotAxes ; ++ i) vMmAxGrp[i].Add( AxesVal[i]) ; Point3d ptAxEnd( AxesVal[0], AxesVal[1], AxesVal[2]) ; // se arco, determino anche alcuni valori intermedi if ( pCamData->IsArc()) { // dati dell'arco Point3d ptCen = pCamData->GetAxesCen() ; double dAngCen = pCamData->GetAxesAngCen() ; Vector3d vtN = pCamData->GetAxesNormDir() ; double dDeltaN = ( ptAxEnd - ptCen) * vtN ; // ricavo i punti const double ANG_STEP = 4.9 ; int nStep = 1 + int( abs( dAngCen) / ANG_STEP) ; double dCoeff = -1. / nStep ; double dCosRot = cos( dCoeff * dAngCen * DEGTORAD) ; double dSinRot = sin( dCoeff * dAngCen * DEGTORAD) ; Point3d ptAxT = ptAxEnd ; for ( int i = 1 ; i < nStep ; ++ i) { // assi lineari ptAxT.Rotate( ptCen, vtN, dCosRot, dSinRot) ; ptAxT.Translate( vtN * ( dCoeff * dDeltaN)) ; for ( int i = 0 ; i < min( nLinAxes, 3) ; ++ i) vMmAxGrp[i].Add( ptAxT.v[i]) ; } } } DBLVECTOR vMinAxGrp( nTotAxes, +INFINITO), vMaxAxGrp( nTotAxes, -INFINITO) ; for ( int i = 0 ; i < nTotAxes ; ++ i) { vMmAxGrp[i].GetMinMax( vMinAxGrp[i], vMaxAxGrp[i]) ; vMmAxAll[i].Add( vMmAxGrp[i]) ; } m_pGeomDB->SetInfo( nClPathId, KEY_PAXMIN, vMinAxGrp) ; m_pGeomDB->SetInfo( nClPathId, KEY_PAXMAX, vMaxAxGrp) ; nClPathId = m_pGeomDB->GetNext( nClPathId) ; } DBLVECTOR vMinAxAll( nTotAxes, +INFINITO), vMaxAxAll( nTotAxes, -INFINITO) ; for ( int i = 0 ; i < nTotAxes ; ++ i) vMmAxAll[i].GetMinMax( vMinAxAll[i], vMaxAxAll[i]) ; m_pGeomDB->SetInfo( nClId, KEY_MAXMIN, vMinAxAll) ; m_pGeomDB->SetInfo( nClId, KEY_MAXMAX, vMaxAxAll) ; return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::GetInitialAxesValues( bool bSkipClimb, bool bMain, DBLVECTOR& vAxVal) const { const CamData* pCamData = GetInitialCamData( bSkipClimb, bMain) ; vAxVal = ( pCamData != nullptr ? pCamData->GetAxesVal() : DBLVECTOR()) ; return ( vAxVal.size() > 0) ; } //---------------------------------------------------------------------------- bool Operation::GetClPathInitialAxesValues( int nClPathId, bool bSkipClimb, DBLVECTOR& vAxVal) const { const CamData* pCamData = GetClPathInitialCamData( nClPathId, bSkipClimb) ; vAxVal = ( pCamData != nullptr ? pCamData->GetAxesVal() : DBLVECTOR()) ; return ( vAxVal.size() > 0) ; } //---------------------------------------------------------------------------- bool Operation::GetFinalAxesValues( bool bSkipRise, bool bMain, DBLVECTOR& vAxVal) const { const CamData* pCamData = GetFinalCamData( bSkipRise, bMain) ; vAxVal = ( pCamData != nullptr ? pCamData->GetAxesVal() : DBLVECTOR()) ; return ( vAxVal.size() > 0) ; } //---------------------------------------------------------------------------- bool Operation::GetClPathFinalAxesValues( int nClPathId, bool bSkipRise, DBLVECTOR& vAxVal) const { const CamData* pCamData = GetClPathFinalCamData( nClPathId, bSkipRise) ; vAxVal = ( pCamData != nullptr ? pCamData->GetAxesVal() : DBLVECTOR()) ; return ( vAxVal.size() > 0) ; } //---------------------------------------------------------------------------- bool Operation::GetInitialToolDir( bool bSkipClimb, Vector3d& vtTool) const { const CamData* pCamData = GetInitialCamData( bSkipClimb) ; vtTool = ( pCamData != nullptr ? pCamData->GetToolDir() : V_NULL) ; return ( ! vtTool.IsSmall()) ; } //---------------------------------------------------------------------------- bool Operation::GetClPathInitialToolDir( int nClPathId, bool bSkipClimb, Vector3d& vtTool) const { const CamData* pCamData = GetClPathInitialCamData( nClPathId, bSkipClimb) ; vtTool = ( pCamData != nullptr ? pCamData->GetToolDir() : V_NULL) ; return ( ! vtTool.IsSmall()) ; } //---------------------------------------------------------------------------- bool Operation::GetFinalToolDir( bool bSkipRise, Vector3d& vtTool) const { const CamData* pCamData = GetFinalCamData( bSkipRise) ; vtTool = ( pCamData != nullptr ? pCamData->GetToolDir() : V_NULL) ; return ( ! vtTool.IsSmall()) ; } //---------------------------------------------------------------------------- bool Operation::GetClPathFinalToolDir( int nClPathId, bool bSkipRise, Vector3d& vtTool) const { const CamData* pCamData = GetClPathFinalCamData( nClPathId, bSkipRise) ; vtTool = ( pCamData != nullptr ? pCamData->GetToolDir() : V_NULL) ; return ( ! vtTool.IsSmall()) ; } //---------------------------------------------------------------------------- const CamData* Operation::GetInitialCamData( bool bSkipClimb, bool bMain) const { // recupero il primo percorso CL non vuoto int nClPathId = GetFirstFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return nullptr ; // recupero i dati Cam della prima entità del percorso return GetClPathInitialCamData( nClPathId, bSkipClimb) ; } //---------------------------------------------------------------------------- const CamData* Operation::GetClPathInitialCamData( int nClPathId, bool bSkipClimb) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return nullptr ; // recupero la prima entità di questo percorso int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; // se richiesto di ignorare le entità CLIMB if ( bSkipClimb) { while ( nEntId != GDB_ID_NULL) { string sName ; if ( ! m_pGeomDB->GetName( nEntId, sName) || sName != MCH_CL_CLIMB) break ; nEntId = m_pGeomDB->GetNext( nEntId) ; } } // recupero i dati Cam dell'entità return GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; } //---------------------------------------------------------------------------- const CamData* Operation::GetFinalCamData( bool bSkipRise, bool bMain) const { // recupero l'ultimo percorso CL non vuoto int nClPathId = GetLastFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return nullptr ; // recupero i dati Cam dell'ultima entità del percorso return GetClPathFinalCamData( nClPathId, bSkipRise) ; } //---------------------------------------------------------------------------- const CamData* Operation::GetClPathFinalCamData( int nClPathId, bool bSkipRise) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return nullptr ; // recupero l'ultima entità di questo percorso int nEntId = m_pGeomDB->GetLastInGroup( nClPathId) ; // se richiesto di ignorare le entità RISE if ( bSkipRise) { while ( nEntId != GDB_ID_NULL) { string sName ; if ( ! m_pGeomDB->GetName( nEntId, sName) || ( sName != MCH_CL_RISE && sName != MCH_CL_HOME)) break ; nEntId = m_pGeomDB->GetPrev( nEntId) ; } } // recupero i dati Cam dell'entità return GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; } //---------------------------------------------------------------------------- string Operation::ExtractInfo( const string& sNotes, const string& sKey) const { string sInfo ; STRVECTOR vsCmd ; Tokenize( sNotes, ";", vsCmd) ; for ( const auto& sCmd : vsCmd) { // se chiave trovata if ( sCmd.find( sKey) != string::npos) { sInfo = sCmd ; ReplaceString( sInfo, sKey, "") ; break ; } } return sInfo ; } //---------------------------------------------------------------------------- string Operation::ExtractHint( const string& sNotes) const { return ExtractInfo( sNotes, "Hint:") ; } //---------------------------------------------------------------------------- bool Operation::SetBlockedRotAxis( const string& sBlockedAxis, bool bToolSetOk) const { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // cancello bloccaggio attuale m_pMchMgr->ClearRotAxisBlock() ; // se richiesto, imposto l'utensile per i calcoli macchina if ( ! bToolSetOk) { if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; } // se non ci sono bloccaggi, esco if ( sBlockedAxis.empty()) return true ; // recupero numero assi lineari e rotanti correnti int nLinAxes = m_pMchMgr->GetCurrLinAxes() ; int nRotAxes = m_pMchMgr->GetCurrRotAxes() ; // possono essere bloccati più assi, divido la stringa sui ";" STRVECTOR vsAxBlock ; Tokenize( sBlockedAxis, ";", vsAxBlock) ; // ciclo sui diversi bloccaggi bool bOk = true ; for ( const string& sAxBlock : vsAxBlock) { // recupero token/nome asse e valore string sKey, sVal ; Split( sAxBlock, "=", true, sKey, sVal) ; if ( sKey.empty() || sVal.empty()) continue ; double dVal = 0 ; if ( ! FromString( sVal, dVal)) continue ; // lo cerco tra i token degli assi rotanti correnti bool bFound = false ; for ( int i = 0 ; i < nRotAxes ; ++ i) { string sAxToken ; if ( m_pMchMgr->GetCurrMachine()->GetCurrAxisToken( i + nLinAxes, sAxToken) && sKey == Trim( sAxToken, " \t\r\n=")) { string sAxis ; m_pMchMgr->GetCurrMachine()->GetCurrAxisName( i + nLinAxes, sAxis) ; if ( ! m_pMchMgr->SetRotAxisBlock( sAxis, dVal)) bOk = false ; bFound = true ; break ; } } // lo cerco tra gli assi rotanti della macchina if ( ! bFound && ! m_pMchMgr->SetRotAxisBlock( sKey, dVal)) bOk = false ; } return bOk ; } //---------------------------------------------------------------------------- bool Operation::ApplyHintToPrevAxRot( const string& sHint, const Machine* pMch, DBLVECTOR& vAxRotPrec) { // verifico validità macchina if ( pMch == nullptr) return false ; // recupero il numero di assi lineari e rotanti attivi int nLinAxes = pMch->GetCurrLinAxes() ; int nRotAxes = pMch->GetCurrRotAxes() ; // verifico validità vettore assi rotanti precedenti if ( ssize( vAxRotPrec) < nRotAxes) return false ; // eseguo STRVECTOR vsTok ; Tokenize( sHint, ",;", vsTok) ; for ( const auto& sTok : vsTok) { if ( sTok.empty()) continue ; string szKey, szVal ; Split( sTok, "=", true, szKey, szVal) ; Trim( szKey) ; for ( int i = 0 ; i < nRotAxes ; ++ i) { string sAxToken ; if ( pMch->GetCurrAxisToken( nLinAxes + i, sAxToken) && szKey == Trim( sAxToken, " \t\r\n=")) { double dVal ; if ( FromString( szVal, dVal)) { double dOffset = 0 ; pMch->GetCurrAxisOffset( nLinAxes + i, dOffset) ; if ( abs( dOffset) > EPS_ANG_SMALL) dVal -= dOffset ; bool bInvert = false ; pMch->GetCurrAxisInvert( nLinAxes + i, bInvert) ; if ( bInvert) dVal = -dVal ; vAxRotPrec[i] = dVal ; } break ; } } } return true ; } //---------------------------------------------------------------------------- bool Operation::CalculateAxesValues( const string& sHint, bool bSolChExact, double dSingConeAng) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero macchina corrente Machine* pMch = m_pMchMgr->GetCurrMachine() ; if ( pMch == nullptr) return false ; // recupero l'operazione precedente non vuota o che richiede home precedente int nPrevOpId = m_pMchMgr->GetPrevActiveOperation( m_nOwnerId) ; Operation* pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; while ( pPrevOp != nullptr) { if ( ! pPrevOp->IsEmpty() || pPrevOp->NeedPrevHome()) break ; else { nPrevOpId = m_pMchMgr->GetPrevActiveOperation( nPrevOpId) ; pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; } } // recupero l'utensile precedente string sPrevTool ; if ( pPrevOp != nullptr && ! pPrevOp->IsEmpty()) sPrevTool = pPrevOp->GetToolName() ; // imposto l'utensile per i calcoli macchina if ( ! pMch->SetCurrTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // rimuovo posizionamento home da lavorazione precedente se non richiesto (non può più essere l'ultima) if ( pPrevOp != nullptr && ! NeedPrevHome()) pPrevOp->RemoveHome() ; // recupero il numero di assi lineari e rotanti attivi int nLinAxes = pMch->GetCurrLinAxes() ; int nRotAxes = pMch->GetCurrRotAxes() ; // recupero gli angoli home DBLVECTOR vAxRotHome( nRotAxes, 0.) ; DBLVECTOR vAxHome ; pMch->GetAllCurrAxesHomePos( vAxHome) ; for ( int i = 0 ; i < nRotAxes ; ++ i) vAxRotHome[i] = vAxHome[nLinAxes + i] ; // Verifico se nei suggerimenti viene richiesta Home per gli assi bool bHintHome = ( sHint.find( "Home") != string::npos) ; // Assegno gli angoli iniziali DBLVECTOR vAxRotPrec( nRotAxes, 0.) ; DBLVECTOR vAxVal ; // se non richiesti angoli Home e utensile non cambiato, uso gli angoli finali della lavorazione precedente if ( ! bHintHome && GetUserNotesZmax() != 2 && ! sPrevTool.empty() && ! ToolChangeNeeded( *pPrevOp, true, *this, true) && pPrevOp->GetFinalAxesValues( true, true, vAxVal)) { for ( int i = 0 ; i < nRotAxes ; ++ i) vAxRotPrec[i] = vAxVal[nLinAxes + i] ; } // altrimenti uso gli angoli home else { vAxRotPrec = vAxRotHome ; } // se ci sono suggerimenti validi per gli assi rotanti, li applico if ( ! bHintHome && ! sHint.empty()) ApplyHintToPrevAxRot( sHint, pMch, vAxRotPrec) ; // recupero la minima differenza angolare da posizione precedente per stare vicino a posizione home double dAngDeltaMinForHome = pMch->GetAngDeltaMinForHome() ; // applico eventuali blocchi di assi rotanti m_pMchMgr->ApplyRotAxisBlock() ; // applico il criterio di scelta della soluzione quando molteplici pMch->SetSolCh( GetSolCh(), bSolChExact) ; // applico angolo di apertura cono da direzione singolare pMch->SetSingConeAng( dSingConeAng) ; // recupero gruppo della geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // calcolo il valore degli assi macchina di tutti i movimenti bool bOk = true ; if ( pMch->GetCurrKinematicChainType() == KIN_CHAIN_MCENT) { m_pGeomDB->SetInfo( nClId, KEY_KINTYPE, KIN_CHAIN_MCENT) ; int nClPathId = m_pGeomDB->GetFirstGroupInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { if ( ! CalculateClPathMcentAxesValues( nClPathId, dAngDeltaMinForHome, vAxRotHome, vAxRotPrec)) bOk = false ; nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ; } } else if ( pMch->GetCurrKinematicChainType() == KIN_CHAIN_ROBOT) { m_pGeomDB->SetInfo( nClId, KEY_KINTYPE, KIN_CHAIN_ROBOT) ; int nClPathId = m_pGeomDB->GetFirstGroupInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { if ( ! CalculateClPathRobotAxesValues( nClPathId, dAngDeltaMinForHome, vAxRotHome, vAxRotPrec)) bOk = false ; nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ; } } else { m_pGeomDB->SetInfo( nClId, KEY_KINTYPE, KIN_CHAIN_NONE) ; bOk = false ; } return bOk ; } //---------------------------------------------------------------------------- bool Operation::CalculateDoubleAxesValues( const string& sHint, bool bSolChExact, double dSingConeAng) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero macchina corrente Machine* pMch = m_pMchMgr->GetCurrMachine() ; if ( pMch == nullptr) return false ; // non si considera la lavorazione precedente (almeno per ora) // l'utensile per i calcoli macchina deve essere già stato impostato // recupero il numero di assi lineari e rotanti attivi int nLinAxes = pMch->GetCurrLinAxes() ; int nRotAxes = pMch->GetCurrRotAxes() ; // recupero gli angoli home DBLVECTOR vAxRotHome( nRotAxes, 0.) ; DBLVECTOR vAxHome ; pMch->GetAllCurrAxesHomePos( vAxHome) ; for ( int i = 0 ; i < nRotAxes ; ++ i) vAxRotHome[i] = vAxHome[nLinAxes + i] ; // Verifico se nei suggerimenti viene richiesta Home per gli assi bool bHintHome = ( sHint.find( "Home") != string::npos) ; // Assegno gli angoli iniziali DBLVECTOR vAxRotPrec( nRotAxes, 0.) ; DBLVECTOR vAxVal ; // uso gli angoli home vAxRotPrec = vAxRotHome ; // se ci sono suggerimenti validi per gli assi rotanti, li applico if ( ! bHintHome && ! sHint.empty()) ApplyHintToPrevAxRot( sHint, pMch, vAxRotPrec) ; // recupero la minima differenza angolare da posizione precedente per stare vicino a posizione home double dAngDeltaMinForHome = pMch->GetAngDeltaMinForHome() ; // applico eventuali blocchi di assi rotanti m_pMchMgr->ApplyRotAxisBlock() ; // applico il criterio di scelta della soluzione quando molteplici pMch->SetSolCh( GetSolCh(), bSolChExact) ; // applico angolo di apertura cono da direzione singolare pMch->SetSingConeAng( dSingConeAng) ; // recupero gruppo della geometria di lavorazione in doppio ( Double Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_DBL) ; if ( nClId == GDB_ID_NULL) return false ; // calcolo il valore degli assi macchina di tutti i movimenti bool bOk = true ; if ( pMch->GetCurrKinematicChainType() == KIN_CHAIN_MCENT) { int nClPathId = m_pGeomDB->GetFirstGroupInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { if ( ! CalculateClPathMcentAxesValues( nClPathId, dAngDeltaMinForHome, vAxRotHome, vAxRotPrec)) bOk = false ; nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ; } } else if ( pMch->GetCurrKinematicChainType() == KIN_CHAIN_ROBOT) { LOG_ERROR( GetEMkLogger(), "Error : double machining with robot not managed") ; bOk = false ; } else bOk = false ; return bOk ; } //---------------------------------------------------------------------------- bool Operation::CalculateClPathMcentAxesValues( int nClPathId, double dAngDeltaMinForHome, const DBLVECTOR& vAxRotHome, DBLVECTOR& vAxRotPrec) { DBLVECTOR vAxRotPrecOri = vAxRotPrec ; // cancello eventuali punti aggiunti EraseAddedPoints( nClPathId) ; // calcolo int nOutStrC = 0 ; if ( ! MyCalculateClPathMcentAxesValues( nClPathId, dAngDeltaMinForHome, vAxRotHome, vAxRotPrec, true, nOutStrC)) { // se attivata scelta angolo iniziale vicino ad home ed extra corsa C, provo a rifarlo disattivando if ( dAngDeltaMinForHome < ANG_FULL && nOutStrC != 0) { // cancello eventuali punti aggiunti EraseAddedPoints( nClPathId) ; // ricalcolo m_pMchMgr->GetCurrMachine()->ResetOutstrokeInfo() ; if ( MyCalculateClPathMcentAxesValues( nClPathId, INFINITO, vAxRotHome, vAxRotPrec, false, nOutStrC)) return true ; } // se extracorsa dell'asse C, provo a precaricarlo al contrario if ( nOutStrC != 0) { // cancello eventuali punti aggiunti EraseAddedPoints( nClPathId) ; // ricalcolo vAxRotPrec = vAxRotPrecOri ; vAxRotPrec[0] = vAxRotPrecOri[0] + ( nOutStrC > 0 ? - ANG_FULL : ANG_FULL) ; m_pMchMgr->GetCurrMachine()->ResetOutstrokeInfo() ; if ( ! MyCalculateClPathMcentAxesValues( nClPathId, INFINITO, vAxRotHome, vAxRotPrec, false, nOutStrC)) return false ; } else return false ; } return true ; } //---------------------------------------------------------------------------- bool Operation::EraseAddedPoints( int nClPathId) { int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; while ( nEntId != GDB_ID_NULL) { // salvo successivo int nNextId = m_pGeomDB->GetNext( nEntId) ; // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; // se punto aggiunto, lo elimino if ( pCamData != nullptr && pCamData->GetFlag2() == -1) m_pGeomDB->Erase( nEntId) ; nEntId = nNextId ; } return true ; } //---------------------------------------------------------------------------- bool Operation::MyCalculateClPathMcentAxesValues( int nClPathId, double dAngDeltaMinForHome, const DBLVECTOR& vAxRotHome, DBLVECTOR& vAxRotPrec, bool bFirstTry, int& nOutStrC) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; Machine* pMachine = m_pMchMgr->GetCurrMachine() ; if ( pMachine == nullptr) return false ; // predispongo variabile per valori assi DBLVECTOR vAxVal ; vAxVal.reserve( 8) ; // valori del precedente Point3d ptPrec ; Vector3d vtDirPrec ; Vector3d vtAuxPrec ; Vector3d vtCorrPrec ; DBLVECTOR vAxPrec ; int nFlag2Prec = 0 ; bool bToolShowPrec = true ; // ciclo su tutte le entità del percorso CL nOutStrC = 0 ; bool bOk = true ; bool bFirst = true ; for ( int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; nEntId != GDB_ID_NULL ; nEntId = m_pGeomDB->GetNext( nEntId)) { // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) continue ; // verifico che il primo movimento non sia un arco if ( bFirst && pCamData->IsArc()) { pCamData->ResetAxes() ; LOG_ERROR( GetEMkLogger(), "Error : first move in ClPath is an arc") return false ; } // calcolo gli assi rotanti della macchina Vector3d vtDir = pCamData->GetToolDir() ; Vector3d vtAux = pCamData->GetAuxDir() ; Vector3d vtCorr = pCamData->GetCorrDir() ; DBLVECTOR vAxRot ; if ( ! CalculateMcentRotAxesValues( bFirst, vtDir, vtAux, dAngDeltaMinForHome, vAxRotHome, vAxRotPrec, vAxRot)) { pCamData->SetAxes( CamData::AS_DIR_ERR, vAxVal) ; LOG_ERROR( GetEMkLogger(), "Error : tool direction unreachable") return false ; } // ricavo posizione con eventuali modifiche dipendenti dalla lavorazione Point3d ptP = pCamData->GetEndPoint() ; AdjustEndPointForAxesCalc( pCamData, ptP) ; // calcolo gli assi lineari della macchina double dX, dY, dZ ; bool bLOk = m_pMchMgr->GetCalcPositions( ptP, vAxRot, dX, dY, dZ) ; if ( ! bLOk) { bOk = false ; pCamData->SetAxes( CamData::AS_ERR, vAxVal) ; continue ; } // assegno valori assi vAxVal.clear() ; vAxVal.emplace_back( dX) ; vAxVal.emplace_back( dY) ; vAxVal.emplace_back( dZ) ; for ( auto dAng : vAxRot) vAxVal.emplace_back( dAng) ; // verifico i limiti di corsa degli assi int nStat ; bool bOsOk = m_pMchMgr->VerifyOutstroke( dX, dY, dZ, vAxRot, false, nStat) ; if ( ! bOsOk || nStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_OUTSTROKE, vAxVal) ; if ( nOutStrC == 0) { if ( (nStat & 64) != 0) nOutStrC = - 1 ; else if ( (nStat & 128) != 0) nOutStrC = 1 ; } string sInfo = "Outstroke : " + m_pMchMgr->GetOutstrokeInfo() ; LOG_INFO( GetEMkLogger(), sInfo.c_str()) // se primo calcolo ed extra corsa asse C esco, altrimenti continuo if ( bFirstTry && nOutStrC != 0) return bOk ; // calcolo e salvo il valore dell'asse ausiliario di testa riportato sul grezzo Vector3d vtBackAux ; pMachine->GetBackAuxDirFromAngles( vAxRot, vtBackAux) ; pCamData->SetBackAuxDir( vtBackAux) ; // imposto stato bFirst = false ; // memorizzo i precedenti ptPrec = ptP ; vtDirPrec = vtDir ; vtAuxPrec = vtAux ; vtCorrPrec = vtCorr ; vAxPrec = vAxVal ; nFlag2Prec = pCamData->GetFlag2() ; bToolShowPrec = pCamData->GetToolShow() ; // memorizzo i valori degli angoli come nuovi precedenti vAxRotPrec = vAxRot ; continue ; } // salvo i valori degli assi pCamData->SetAxes( CamData::AS_OK, vAxVal) ; // calcolo e salvo il valore dell'asse ausiliario di testa riportato sul grezzo Vector3d vtBackAux ; pMachine->GetBackAuxDirFromAngles( vAxRot, vtBackAux) ; pCamData->SetBackAuxDir( vtBackAux) ; // tipo di movimento int nMoveType = pCamData->GetMoveType() ; // se punto finale di linea if ( ! bFirst && pCamData->IsLine()) { // Verifica ricorsiva del punto medio (se non già fatta) bool bMidAdded = false ; if ( nFlag2Prec == -1 || pCamData->GetFlag2() == -1) bMidAdded = true ; else { bool bAxesError ; if ( ! VerifyMcentLineMidPoint( ptPrec, vtDirPrec, vtAuxPrec, vtCorrPrec, vAxPrec, ptP, vtDir, vtAux, vtCorr, vAxVal, 1, nEntId, nMoveType, bToolShowPrec && pCamData->GetToolShow(), bMidAdded, bAxesError)) { string sOut = "Error : VerifyLineMidPoint of CalculateClPathAxesValues failed (EntId=" + ToString( nEntId) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) return false ; } if ( bAxesError) bOk = false ; } // Se esistono aree protette, devo comunque verificare i punti intermedi if ( ! bMidAdded && pMachine->ExistProtectedAreas()) { // verifico i limiti di corsa dei punti lungo la linea Point3d ptPrec( vAxPrec[0], vAxPrec[1], vAxPrec[2]) ; Point3d ptP( dX, dY, dZ) ; double dLenMove = ApproxDist( ptPrec, ptP) ; constexpr double PA_VERIF_LEN = 10 ; constexpr int MAX_VERIF_STEP = 16 ; const int nNumStep = max( min( int( dLenMove / PA_VERIF_LEN), MAX_VERIF_STEP), 1) ; for ( int i = 1 ; i < nNumStep ; ++ i) { double dCoeff = double( i) / nNumStep ; Point3d ptCurr = Media( ptPrec, ptP, dCoeff) ; DBLVECTOR vAng( vAxRot.size()) ; for ( int j = 0 ; j < ssize( vAxRot) ; ++ j) vAng[j] = vAxRotPrec[j] * ( 1 - dCoeff) + vAxRot[j] * dCoeff ; int nStat = 0 ; bool bPaOk = pMachine->VerifyProtectedAreas( ptCurr.x, ptCurr.y, ptCurr.z, vAng, nStat) ; if ( ! bPaOk || nStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_OUTSTROKE, vAxVal) ; string sInfo = "Outstroke : " + m_pMchMgr->GetOutstrokeInfo() ; LOG_INFO( GetEMkLogger(), sInfo.c_str()) } } } } // se punto finale di arco -> devo calcolarne il centro in assi macchina else if ( pCamData->IsArc()) { // devo lavorare con arco schiacciato nel suo piano Point3d ptCen = pCamData->GetCenter() ; AdjustArcCenterForAxesCalc( pCamData, ptCen) ; double dAngCen = pCamData->GetAngCen() ; Vector3d vtN = pCamData->GetNormDir() ; double dDeltaN = pCamData->GetDeltaN() ; // ricavo punto medio nel piano dell'arco Point3d ptMid = ptP - dDeltaN * vtN ; ptMid.Rotate( ptCen, vtN, - dAngCen / 2) ; // determino i valori degli assi al punto medio DBLVECTOR vAngMid( vAxRot.size()) ; for ( int j = 0 ; j < ssize( vAxRot) ; ++ j) vAngMid[j] = ( vAxRotPrec[j] + vAxRot[j]) / 2 ; double dXmid, dYmid, dZmid ; bool bLmidOk = m_pMchMgr->GetCalcPositions( ptMid, vAngMid, dXmid, dYmid, dZmid) ; if ( ! bLmidOk) { bOk = false ; pCamData->SetAxes( CamData::AS_ERR, vAxVal) ; continue ; } // ricavo punto finale nel piano dell'arco Point3d ptEnd = ptP - dDeltaN * vtN ; // determino i valori degli assi al punto finale DBLVECTOR vAngEnd( vAxRot.size()) ; for ( int j = 0 ; j < ssize( vAxRot) ; ++ j) vAngEnd[j] = vAxRot[j] ; double dXend, dYend, dZend ; bool bLendOk = m_pMchMgr->GetCalcPositions( ptEnd, vAngEnd, dXend, dYend, dZend) ; if ( ! bLendOk) { bOk = false ; pCamData->SetAxes( CamData::AS_ERR, vAxVal) ; continue ; } // punti espressi in assi macchina Point3d ptP1M( vAxPrec[0], vAxPrec[1], vAxPrec[2]) ; Point3d ptP2M( dXmid, dYmid, dZmid) ; Point3d ptP3M( dXend, dYend, dZend) ; // se coincidono if ( AreSamePointApprox( ptP1M, ptP2M) && AreSamePointApprox( ptP1M, ptP3M)) { // lo considero un arco di raggio nullo pCamData->SetAxesCen( ptP2M) ; pCamData->SetAxesRad( 0) ; pCamData->SetAxesAngCen( 0) ; pCamData->SetAxesNormDir( V_NULL) ; // il movimento diventa lineare pCamData->SetMoveType( 1) ; // non è necessario verificare i limiti di corsa degli assi } // altrimenti calcolo l'arco cui appartengono else { // calcolo arco per i tre punti espressi in assi macchina PtrOwner pCurve( GetArc3P( ptP1M, ptP2M, ptP3M, false)) ; if ( IsNull( pCurve)) { LOG_ERROR( GetEMkLogger(), "Error : arc on machine not calculable") return false ; } // se realmente arco if ( pCurve->GetType() == CRV_ARC) { ICurveArc* pArc = GetCurveArc( pCurve) ; // verifico il piano (il vettore va portato nel riferimento degli assi lineari) Vector3d vtRef ; m_pMchMgr->GetCalcPartDirFromAngles( pCamData->GetNormDir(), vAngEnd, vtRef) ; vtRef.ToLoc( m_pMchMgr->GetCurrLinAxesFrame()) ; if ( pArc->GetNormVersor() * vtRef < 0) pArc->InvertN() ; // assegno il centro e il raggio di questo arco ai dati cam pCamData->SetAxesCen( pArc->GetCenter()) ; pCamData->SetAxesRad( pArc->GetRadius()) ; pCamData->SetAxesAngCen( pArc->GetAngCenter()) ; pCamData->SetAxesNormDir( pArc->GetNormVersor()) ; // aggiorno il tipo di arco pCamData->SetMoveType( pArc->GetAngCenter() > 0 ? 3 : 2) ; // verifico i limiti di corsa dei punti lungo l'arco const int NUM_VERIF_STEP = 16 ; Point3d ptCen = pCamData->GetAxesCen() ; double dAngCenStep = pCamData->GetAxesAngCen() / NUM_VERIF_STEP ; Vector3d vtN = pCamData->GetAxesNormDir() ; Vector3d vtCurr = Point3d( vAxPrec[0], vAxPrec[1], vAxPrec[2]) - ptCen ; for ( int i = 1 ; i < NUM_VERIF_STEP ; ++ i) { vtCurr.Rotate( vtN, dAngCenStep) ; double dCoeff = double( i) / NUM_VERIF_STEP ; Point3d ptCurr = ptCen + vtCurr + vtN * dDeltaN * dCoeff ; DBLVECTOR vAng( vAxRot.size()) ; for ( int j = 0 ; j < ssize( vAxRot) ; ++ j) vAng[j] = vAxRotPrec[j] * ( 1 - dCoeff) + vAxRot[j] * dCoeff ; int nStat ; bool bOsOk = m_pMchMgr->VerifyOutstroke( ptCurr.x, ptCurr.y, ptCurr.z, vAng, false, nStat) ; if ( ! bOsOk || nStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_OUTSTROKE, vAxVal) ; string sInfo = "Outstroke : " + m_pMchMgr->GetOutstrokeInfo() ; LOG_INFO( GetEMkLogger(), sInfo.c_str()) } } } // altrimenti linea else { // lo considero un arco di raggio infinito pCamData->SetAxesCen( ORIG) ; pCamData->SetAxesRad( 0) ; pCamData->SetAxesAngCen( 0) ; pCamData->SetAxesNormDir( V_NULL) ; // il movimento diventa lineare pCamData->SetMoveType( 1) ; // non è necessario verificare i limiti di corsa degli assi } } } bFirst = false ; // memorizzo i precedenti ptPrec = ptP ; vtDirPrec = vtDir ; vtAuxPrec = vtAux ; vtCorrPrec = vtCorr ; vAxPrec = vAxVal ; vAxRotPrec = vAxRot ; nFlag2Prec = pCamData->GetFlag2() ; bToolShowPrec = pCamData->GetToolShow() ; } return bOk ; } //---------------------------------------------------------------------------- bool Operation::CalculateMcentRotAxesValues( bool bFirst, const Vector3d& vtTool, const Vector3d& vtAux, double dAngDeltaMinForHome, const DBLVECTOR& vAxRotHome, const DBLVECTOR& vAxRotPrec, DBLVECTOR& vAxRot) { // macchina Machine* pMachine = m_pMchMgr->GetCurrMachine() ; if ( pMachine == nullptr) return false ; // continuità su assi rotanti nei movimenti successivi al primo per tutte le lavorazioni, tranne disposizioni bool bRotContOnNext = ( GetType() != OPER_DISP) ; // massima variazione secondo asse rotante per continuità const double dMaxDeltaR2 = 30 ; // imposto primo asse precedente (per caso con 3 rotanti liberi) pMachine->SetPrevAngA( ( ! vAxRotPrec.empty() ? vAxRotPrec[0] : NAN)) ; // calcolo degli assi rotanti della macchina int nRStat ; DBLVECTOR vAng1, vAng2 ; bool bROk = pMachine->GetAngles( vtTool, vtAux, nRStat, vAng1, vAng2) ; pMachine->SetPrevAngA( NAN) ; if ( ! bROk || nRStat == 0) return false ; if ( abs( nRStat) == 1) { // se primo movimento if ( bFirst) { // porto gli angoli ai valori più vicini ai precedenti con offset di uno o più giri for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { if ( pMachine->IsKinematicRotAxisBlocked( i)) continue ; pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng1[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente, limitandolo alla corsa if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { // ignoro gli assi bloccati if ( pMachine->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; pMachine->LimitAngleToStroke( i, vAng1[i]) ; break ; } } } // per movimenti successivi else { // scelgo gli angoli più vicini, per continuità non applico offset per stare nelle corse for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { if ( bRotContOnNext) vAng1[i] = AngleNearAngle( vAng1[i], vAxRotPrec[i]) ; else pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng1[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { // ignoro gli assi bloccati if ( pMachine->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; break ; } } } } if ( abs( nRStat) == 2) { bool bAng1 = true ; bool bAng2 = true ; // se primo movimento if ( bFirst) { // porto gli angoli ai valori più vicini ai precedenti con offset di uno o più giri for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng1[i]) ; pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng2[i]) ; if ( abs( vAng2[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng2[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente, limitandolo alla corsa if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { // ignoro gli assi bloccati if ( pMachine->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; pMachine->LimitAngleToStroke( i, vAng1[i]) ; vAng2[i] = vAng1[i] ; break ; } } } else { // scelgo gli angoli più vicini, per continuità non applico offset per stare nelle corse for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { if ( bRotContOnNext) vAng1[i] = AngleNearAngle( vAng1[i], vAxRotPrec[i]) ; else pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng1[i]) ; if ( bRotContOnNext) vAng2[i] = AngleNearAngle( vAng2[i], vAxRotPrec[i]) ; else pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng2[i]) ; if ( abs( vAng2[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng2[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { // ignoro gli assi bloccati if ( pMachine->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; vAng2[i] = vAng1[i] ; break ; } } // verifico che le soluzioni siano nelle corse for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { if ( ! pMachine->VerifyAngleOutstroke( i, vAng1[i])) bAng1 = false ; if ( ! pMachine->VerifyAngleOutstroke( i, vAng2[i])) bAng2 = false ; } } // scelgo la soluzione più vicina alla precedente double dDelta1 = 0 ; double dDelta2 = 0 ; for ( int i = 0 ; i < ssize( vAng1) ; ++ i) { // ignoro gli assi bloccati if ( pMachine->IsKinematicRotAxisBlocked( i)) continue ; // calcolo i delta asse con eventuale peso bool bFirst = ( i == 0) ; dDelta1 += abs( vAng1[i] - vAxRotPrec[i]) * ( bFirst ? pMachine->GetCurrRot1W() : 1) ; dDelta2 += abs( vAng2[i] - vAxRotPrec[i]) * ( bFirst ? pMachine->GetCurrRot1W() : 1) ; } if ( bAng2 && ( dDelta2 < dDelta1 - 10 * EPS_ANG_SMALL || ! bAng1)) swap( vAng1, vAng2) ; // se imposto limite su variazione di secondo asse rotante if ( ( pMachine->GetCurrCalcMaxDeltaR2OnFirst() || ! bFirst) && dMaxDeltaR2 > EPS_ANG_SMALL && vAng1.size() >= 2) { double dR2Diff1 = abs( vAng1[1] - vAxRotPrec[1]) ; double dR2Diff2 = abs( vAng2[1] - vAxRotPrec[1]) ; if ( dR2Diff1 > dR2Diff2 + 10 * EPS_ANG_SMALL && dR2Diff1 > dMaxDeltaR2 && ( dR2Diff2 < dMaxDeltaR2 || dR2Diff2 < dR2Diff1 - dMaxDeltaR2)) swap( vAng1, vAng2) ; } } vAxRot = vAng1 ; return true ; } //---------------------------------------------------------------------------- bool Operation::VerifyMcentLineMidPoint( const Point3d& ptPrec, const Vector3d& vtDirPrec, const Vector3d& vtAuxPrec, const Vector3d& vtCorrPrec, const DBLVECTOR& vAxPrec, const Point3d& ptP, const Vector3d& vtDir, const Vector3d& vtAux, const Vector3d& vtCorr, const DBLVECTOR& vAxVal, int nCnt, int nEntId, int nMoveType, bool bToolShow, bool& bAdded, bool& bAxError) { // impostazioni bAdded = false ; bAxError = false ; Machine* pMachine = m_pMchMgr->GetCurrMachine() ; // se disposizione non vanno fatti controlli if ( GetType() == OPER_DISP) return true ; // se superato il limite di ricursioni, non devo fare alcunché const int MAX_RECURSION = 8 ; if ( nCnt > MAX_RECURSION) return true ; // calcolo variazione angolare double dDeltaAng = 0 ; for ( int i = 3 ; i < int( vAxVal.size()) ; ++i) dDeltaAng += abs( vAxVal[i] - vAxPrec[i]) ; // Se non cambia la direzione utensile, non devo fare alcunché const double MAX_DELTA_ANG = 0.01 ; if ( dDeltaAng <= MAX_DELTA_ANG) return true ; // Determino i valori degli assi al punto medio DBLVECTOR vAxMid( vAxVal.size()) ; for ( int i = 0 ; i < int( vAxVal.size()) ; ++ i) vAxMid[i] = ( vAxPrec[i] + vAxVal[i]) / 2 ; DBLVECTOR vAxRotMid( int( vAxVal.size() - 3)) ; for ( int i = 3 ; i < int( vAxVal.size()) ; ++ i) vAxRotMid[i-3] = vAxMid[i] ; // ricavo il punto medio del movimento Point3d ptMoveMid ; if ( ! pMachine->GetTipFromPositions( vAxMid[0], vAxMid[1], vAxMid[2], vAxRotMid, false, false, true, ptMoveMid)) return false ; // verifico le differenze double dDist ; if ( ! DistPointLine( ptMoveMid, ptPrec, ptP).GetDist( dDist)) return false ; double dLinTol = GetApproxLinTol() ; if ( nMoveType == 0) dLinTol = max( 10 * dLinTol, LIN_TOL_RAW) ; else dLinTol = max( dLinTol, 2 * EPS_SMALL) ; bool bInTol = ( dDist <= dLinTol) ; if ( ExeGetDebugLevel() >= 5) { string sOut = "Midpoint dist = " + ToString( dDist, -4) + ( nMoveType == 0 ? " (G0" : " (G1") + " Cnt=" + ToString( nCnt) + ") " + ( bInTol ? "Ok" : "Split") ; LOG_INFO( GetEMkLogger(), sOut.c_str()) } // Se posizione in tolleranza, non devo fare alcunché if ( bInTol) return true ; // Va inserito il punto medio bAdded = true ; Point3d ptMid = Media( ptPrec, ptP) ; Vector3d vtDirMid = Media( vtDirPrec, vtDir) ; if ( ! vtDirMid.Normalize()) { if ( ! pMachine->GetBackToolDirFromAngles( vAxRotMid, vtDirMid)) return ( nMoveType == 0) ; } Vector3d vtAuxMid = Media( vtAuxPrec, vtAux) ; if ( ! vtAuxPrec.IsSmall() && ! vtAux.IsSmall() && ! vtAuxMid.Normalize()) { if ( ! pMachine->GetBackAuxDirFromAngles( vAxRotMid, vtAuxMid)) return ( nMoveType == 0) ; } Vector3d vtCorrMid = Media( vtCorrPrec, vtCorr) ; if ( ! vtCorrPrec.IsSmall() && ! vtCorr.IsSmall() && ! vtCorrMid.Normalize()) { // assegno il versore correzione precedente vtCorrMid = vtCorrPrec ; // se possibile, cerco di migliorarlo considerando i rif. prec. e sul medio da direzioni fresa (Z) e ausiliaria (X) Frame3d refPrec, refMid ; if ( refPrec.Set( ORIG, vtDirPrec, vtAuxPrec) && refMid.Set( ORIG, vtDirMid, vtAuxMid)) { // versore correzione in locale al precente coincide con quello in locale al corrente vtCorrMid.ToLoc( refPrec) ; vtCorrMid.ToGlob( refMid) ; } } // inserisco nuova entità punto (per evitare problemi di lunghezza linea) // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( ptMid) ; // inserisco l'oggetto nel DB geometrico int nMidEntId = m_pGeomDB->InsertGeoObj( GDB_ID_NULL, nEntId, GDB_BEFORE, Release( pGP)) ; if ( nMidEntId == GDB_ID_NULL) return false ; // creo oggetto dati Cam const CamData* pEntCam = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pEntCam == nullptr) return false ; PtrOwner pCam( pEntCam->Clone()) ; if ( IsNull( pCam)) return false ; // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nMidEntId, Release( pCam)) ; // assegno valori CamData* pMidCamData = GetCamData( m_pGeomDB->GetUserObj( nMidEntId)) ; if ( pMidCamData == nullptr) return false ; pMidCamData->SetEndPoint( ptMid) ; pMidCamData->SetToolDir( vtDirMid) ; pMidCamData->SetAuxDir( vtAuxMid) ; pMidCamData->SetCorrDir( vtCorrMid) ; pMidCamData->SetFlag2( -1) ; pMidCamData->SetToolShow( bToolShow && nCnt < 4) ; // calcolo gli assi rotanti per il punto medio DBLVECTOR vAxRotHome( int( vAxVal.size() - 3)) ; DBLVECTOR vAxRotPrec( int( vAxVal.size() - 3)) ; for ( int i = 3 ; i < int( vAxVal.size()) ; ++ i) vAxRotPrec[i-3] = vAxVal[i] ; if ( ! CalculateMcentRotAxesValues( false, vtDirMid, vtAuxMid, INFINITO, vAxRotHome, vAxRotPrec, vAxRotMid)) { pMidCamData->SetAxes( CamData::AS_DIR_ERR, vAxVal) ; return false ; } for ( int i = 3 ; i < int( vAxVal.size()) ; ++ i) vAxMid[i] = vAxRotMid[i-3] ; // determino il nuovo valore degli assi lineari sul punto medio double dMidX, dMidY, dMidZ ; bool bMidLOk = m_pMchMgr->GetCalcPositions( ptMid, vAxRotMid, dMidX, dMidY, dMidZ) ; if ( ! bMidLOk) { pMidCamData->SetAxes( CamData::AS_ERR, vAxMid) ; bAxError = true ; return true ; } // verifico i limiti di corsa degli assi int nStat ; bool bOsOk = m_pMchMgr->VerifyOutstroke( dMidX, dMidY, dMidZ, vAxRotMid, false, nStat) ; if ( ! bOsOk || nStat != 0) { pMidCamData->SetAxes( CamData::AS_OUTSTROKE, vAxMid) ; bAxError = true ; return true ; } // assegno i valori degli assi vAxMid[0] = dMidX ; vAxMid[1] = dMidY ; vAxMid[2] = dMidZ ; pMidCamData->SetAxes( CamData::AS_OK, vAxMid) ; // calcolo e salvo il valore dell'asse ausiliario di testa riportato sul grezzo Vector3d vtBackAux ; pMachine->GetBackAuxDirFromAngles( vAxRotMid, vtBackAux) ; pMidCamData->SetBackAuxDir( vtBackAux) ; // devo verificare le due nuove parti ++ nCnt ; bool bAdded1, bAxError1, bAdded2, bAxError2 ; if ( ! VerifyMcentLineMidPoint( ptPrec, vtDirPrec, vtAuxPrec, vtCorrPrec, vAxPrec, ptMid, vtDirMid, vtAuxMid, vtCorrMid, vAxMid, nCnt, nMidEntId, nMoveType, bToolShow, bAdded1, bAxError1)) return false ; if ( bAxError1) bAxError = true ; if ( ! VerifyMcentLineMidPoint( ptMid, vtDirMid, vtAuxMid, vtCorrMid, vAxMid, ptP, vtDir, vtAux, vtCorr, vAxVal, nCnt, nEntId, nMoveType, bToolShow, bAdded2, bAxError2)) return false ; if ( bAxError2) bAxError = true ; return true ; } //---------------------------------------------------------------------------- bool Operation::CalculateClPathRobotAxesValues( int nClPathId, double dAngDeltaMinForHome, const DBLVECTOR& vAxRotHome, DBLVECTOR& vAxRotPrec) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // cancello eventuali punti aggiunti EraseAddedPoints( nClPathId) ; // eventuale direzione Aux preferenziale int nSolCh = GetSolCh() ; bool bUseRefAux = true ; Vector3d vtRefAux = V_NULL ; switch ( nSolCh) { case MCH_SCC_ADIR_XP : vtRefAux = X_AX ; break ; case MCH_SCC_ADIR_XM : vtRefAux = -X_AX ; break ; case MCH_SCC_ADIR_YP : vtRefAux = Y_AX ; break ; case MCH_SCC_ADIR_YM : vtRefAux = -Y_AX ; break ; case MCH_SCC_ADIR_ZP : vtRefAux = Z_AX ; break ; case MCH_SCC_ADIR_ZM : vtRefAux = -Z_AX ; break ; default : bUseRefAux = false ; break ; } // valori del precedente Point3d ptPrec ; Vector3d vtDirPrec ; Vector3d vtAuxPrec ; Vector3d vtCorrPrec ; int nFlag2Prec = 0 ; bool bToolShowPrec = true ; // predispongo variabile per valori assi DBLVECTOR vAxVal ; vAxVal.reserve( 8) ; // ciclo su tutte le entità del percorso CL bool bOk = true ; bool bFirst = true ; for ( int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; nEntId != GDB_ID_NULL ; nEntId = m_pGeomDB->GetNext( nEntId)) { // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) continue ; // verifico che il movimento non sia un arco if ( bFirst && pCamData->IsArc()) { pCamData->ResetAxes() ; LOG_ERROR( GetEMkLogger(), "Error : move in ClPath for Robot is an arc") return false ; } // ricavo posizione con eventuali modifiche dipendenti dalla lavorazione Point3d ptP = pCamData->GetEndPoint() ; AdjustEndPointForAxesCalc( pCamData, ptP) ; // recupero direzioni Vector3d vtDir = pCamData->GetToolDir() ; Vector3d vtAux ; if ( ! bUseRefAux) { if (( nSolCh == MCH_SCC_ADIR_NEAR || nSolCh == MCH_SCC_STD) && ! pCamData->GetAuxDir().IsSmall()) { vtAux = OrthoCompo( pCamData->GetAuxDir(), vtDir) ; vtAux.Normalize() ; } else if (( nSolCh == MCH_SCC_ADIR_FAR || nSolCh == MCH_SCC_OPPOSITE) && ! pCamData->GetAuxDir().IsSmall()) { vtAux = - OrthoCompo( pCamData->GetAuxDir(), vtDir) ; vtAux.Normalize() ; } else { // MCH_SCC_NONE (o riconducibili a questo) Frame3d frTool ; frTool.Set( ORIG, vtDir) ; frTool.Rotate( frTool.Orig(), frTool.VersZ(), 0, 1) ; vtAux = frTool.VersX() ; } } else { vtAux = OrthoCompo( vtRefAux, vtDir) ; vtAux.Normalize() ; } pCamData->SetAuxDir( vtAux) ; Vector3d vtCorr = pCamData->GetCorrDir() ; // calcolo gli assi del robot if ( ! CalculateRobotAxesValues( bFirst, ptP, vtDir, vtAux, dAngDeltaMinForHome, vAxRotHome, vAxRotPrec, vAxVal)) { pCamData->SetAxes( CamData::AS_ERR, vAxVal) ; return false ; } // verifico di essere nelle corse degli assi int nStat ; bool bOsOk = m_pMchMgr->VerifyOutstroke( 0, 0, 0, vAxVal, false, nStat) ; if ( ! bOsOk || nStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_OUTSTROKE, vAxVal) ; } else { pCamData->SetAxes( CamData::AS_OK, vAxVal) ; // l'asse ausiliario di testa riportato sul grezzo coincide con quello già impostato pCamData->SetBackAuxDir( vtAux) ; } // tipo di movimento int nMoveType = pCamData->GetMoveType() ; // se non è l'inizio, verifico il punto medio if ( ! bFirst) { // Verifica ricorsiva del punto medio (se non già fatta) bool bMidAdded = false ; if ( nFlag2Prec == -1 || pCamData->GetFlag2() == -1) bMidAdded = true ; else { bool bAxesError ; if ( ! VerifyRobotLineMidPoint( ptPrec, vtDirPrec, vtAuxPrec, vtCorrPrec, vAxRotPrec, ptP, vtDir, vtAux, vtCorr, vAxVal, 1, nEntId, nMoveType, bToolShowPrec && pCamData->GetToolShow(), bMidAdded, bAxesError)) { string sOut = "Error : VerifyLineMidPoint of CalculateClPathAxesValues failed (EntId=" + ToString( nEntId) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) return false ; } if ( bAxesError) bOk = false ; } } // aggiorno valori precedenti bFirst = false ; ptPrec = ptP ; vtDirPrec = vtDir ; vtAuxPrec = vtAux ; vtCorrPrec = vtCorr ; vAxRotPrec = vAxVal ; nFlag2Prec = pCamData->GetFlag2() ; bToolShowPrec = pCamData->GetToolShow() ; } // controllo la continuità del movimento const double MAX_DELTA_ANG = 45 ; bool bRotContOnNext = ( GetType() != OPER_DISP) ; int nInd = 0 ; int nCount = m_pGeomDB->GetGroupObjs( nClPathId) ; const DBLVECTOR* pvPrevAxVal = nullptr ; for ( int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; nEntId != GDB_ID_NULL ; nEntId = m_pGeomDB->GetNext( nEntId)) { // indice del movimento ++ nInd ; // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) continue ; const DBLVECTOR& vCurrAxVal = pCamData->GetAxesVal() ; // se movimento di lavoro ed esiste precedente, li confronto if ( ! pCamData->IsRapid() && pvPrevAxVal != nullptr) { // Se richiesta continuità, verifico non ci siano salti double dDeltaR4 = vCurrAxVal[3] - (*pvPrevAxVal)[3] ; double dDeltaR6 = vCurrAxVal[5] - (*pvPrevAxVal)[5] ; if ( bRotContOnNext && ( abs( dDeltaR4) > MAX_DELTA_ANG || abs( dDeltaR6) > MAX_DELTA_ANG)) { string sOut = "Lost continuity on R4 (Diff=" + ToString( dDeltaR4, 1) + ") or R6 (Diff=" + ToString( dDeltaR6, 1) + ") at move " + ToString( nInd) + " of " + ToString( nCount) ; m_pMchMgr->SetWarning( 1101, sOut) ; } } // salvo nuovi precedenti pvPrevAxVal = &vCurrAxVal ; } return bOk ; } //---------------------------------------------------------------------------- bool Operation::CalculateRobotAxesValues( bool bFirst, const Point3d& ptP, const Vector3d& vtTool, const Vector3d& vtAux, double dAngDeltaMinForHome, const DBLVECTOR& vAxRotHome, const DBLVECTOR& vAxRotPrec, DBLVECTOR& vAxVal) { // macchina Machine* pMachine = m_pMchMgr->GetCurrMachine() ; if ( pMachine == nullptr) return false ; // continuità su assi rotanti nei movimenti successivi al primo per tutte le lavorazioni, tranne disposizioni bool bRotContOnNext = ( GetType() != OPER_DISP) ; // calcolo angoli dei giunti DBLVECTOR vAng1, vAng2 ; bool bRaOk = pMachine->GetRobotAngles( ptP, vtTool, vtAux, vAng1, vAng2) ; if ( ! bRaOk) return false ; // se inizio if ( bFirst) { // porto gli angoli ai valori più vicini ai precedenti con offset di uno o più giri for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng1[i]) ; } for ( int i = 0 ; i < int( vAng2.size()) ; ++ i) { pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng2[i]) ; if ( abs( vAng2[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) pMachine->GetNearestAngleInStroke( i, vAxRotHome[i], vAng2[i]) ; } bFirst = false ; } // altrimenti movimenti successivi else { // porto gli angoli ai valori più vicini ai precedenti con offset di uno o più giri for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { if ( bRotContOnNext) vAng1[i] = AngleNearAngle( vAng1[i], vAxRotPrec[i]) ; else pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; } for ( int i = 0 ; i < int( vAng2.size()) ; ++ i) { if ( bRotContOnNext) vAng2[i] = AngleNearAngle( vAng2[i], vAxRotPrec[i]) ; else pMachine->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng2[i]) ; } // verifico che le soluzioni siano nelle corse bool bAng1 = ( ! vAng1.empty()) ; for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { if ( ! pMachine->VerifyAngleOutstroke( i, vAng1[i])) bAng1 = false ; } bool bAng2 = ( ! vAng2.empty()) ; for ( int i = 0 ; i < int( vAng2.size()) ; ++ i) { if ( ! pMachine->VerifyAngleOutstroke( i, vAng2[i])) bAng2 = false ; } if ( bAng2) { if ( ! bAng1) { vAng1 = vAng2 ; vAng2.clear() ; swap( bAng1, bAng2) ; } } else { vAng2.clear() ; } if ( ! bAng1) { int nStat ; pMachine->VerifyOutstroke( 0, 0, 0, vAng1, true, nStat) ; LOG_ERROR( GetEMkLogger(), pMachine->GetOutstrokeInfo().c_str()) return false ; } } // Scelgo la soluzione più vicina // se esiste solo la prima if ( vAng2.empty()) vAxVal = vAng1 ; // evito passaggio per lo zero di R5 (per evitare passaggio attraverso direzione singolare) else if ( vAng1[4] * vAxRotPrec[4] >= 0 && vAng2[4] * vAxRotPrec[4] < 0) vAxVal = vAng1 ; else if ( vAng1[4] * vAxRotPrec[4] < 0 && vAng2[4] * vAxRotPrec[4] >= 0) vAxVal = vAng2 ; // minimizzo rotazione polso else if ( abs( vAng1[3] - vAxRotPrec[3]) < abs( vAng2[3] - vAxRotPrec[3]) + 10 * EPS_ANG_SMALL) vAxVal = vAng1 ; else vAxVal = vAng2 ; // controllo continuità di R6 dalla posizione precedente pMachine->GetNearestAngleInStroke( 5, vAxRotPrec[5], vAxVal[5]) ; return true ; } //---------------------------------------------------------------------------- bool Operation::VerifyRobotLineMidPoint( const Point3d& ptPrec, const Vector3d& vtDirPrec, const Vector3d& vtAuxPrec, const Vector3d& vtCorrPrec, const DBLVECTOR& vAxPrec, const Point3d& ptP, const Vector3d& vtDir, const Vector3d& vtAux, const Vector3d& vtCorr, const DBLVECTOR& vAxVal, int nCnt, int nEntId, int nMoveType, bool bToolShow, bool& bAdded, bool& bAxError) { // macchina Machine* pMachine = m_pMchMgr->GetCurrMachine() ; if ( pMachine == nullptr) return false ; // impostazioni bAdded = false ; bAxError = false ; // se disposizione non vanno fatti controlli if ( GetType() == OPER_DISP) return true ; // se superato il limite di ricursioni, non devo fare alcunché const int MAX_RECURSION = 8 ; if ( nCnt > MAX_RECURSION) return true ; // calcolo variazione assi double dDeltaAng = 0 ; for ( int i = 0 ; i < int( vAxVal.size()) ; ++i) dDeltaAng += abs( vAxVal[i] - vAxPrec[i]) ; // Se non cambia la direzione utensile, non devo fare alcunché const double MAX_DELTA_ANG = 0.01 ; if ( dDeltaAng <= MAX_DELTA_ANG) return true ; // Determino i valori degli assi al punto medio DBLVECTOR vAxMid( vAxVal.size()) ; for ( int i = 0 ; i < int( vAxVal.size()) ; ++ i) vAxMid[i] = ( vAxPrec[i] + vAxVal[i]) / 2 ; // ricavo il punto medio del movimento Point3d ptMoveMid ; if ( ! pMachine->GetTipFromPositions( 0, 0, 0, vAxMid, false, false, true, ptMoveMid)) return false ; // verifico le differenze double dDist ; if ( ! DistPointLine( ptMoveMid, ptPrec, ptP).GetDist( dDist)) return false ; double dLinTol = GetApproxLinTol() ; if ( nMoveType == 0) dLinTol = max( 10 * dLinTol, LIN_TOL_RAW) ; else dLinTol = max( dLinTol, 2 * EPS_SMALL) ; bool bInTol = ( dDist <= dLinTol) ; if ( ExeGetDebugLevel() >= 5) { string sOut = "Midpoint dist=" + ToString( dDist, -4) + ( nMoveType == 0 ? " (G0" : " (G1") + " Cnt=" + ToString( nCnt) + ") " + ( bInTol ? "Ok" : "Split") ; if ( ! bInTol) sOut += " - Ap=" + ToString( vAxPrec, -2) + " An=" + ToString( vAxVal, -2) ; LOG_INFO( GetEMkLogger(), sOut.c_str()) } // Se posizione in tolleranza, non devo fare alcunché if ( bInTol) return true ; // Va inserito il punto medio bAdded = true ; Point3d ptMid = Media( ptPrec, ptP) ; Vector3d vtDirMid = Media( vtDirPrec, vtDir) ; if ( ! vtDirMid.Normalize()) { if ( ! pMachine->GetBackToolDirFromAngles( vAxMid, vtDirMid)) return ( nMoveType == 0) ; } Vector3d vtAuxMid = Media( vtAuxPrec, vtAux) ; if ( ! vtAuxPrec.IsSmall() && ! vtAux.IsSmall() && ! vtAuxMid.Normalize()) { if ( ! pMachine->GetBackAuxDirFromAngles( vAxMid, vtAuxMid)) return ( nMoveType == 0) ; } Vector3d vtCorrMid = Media( vtCorrPrec, vtCorr) ; if ( ! vtCorrPrec.IsSmall() && ! vtCorr.IsSmall() && ! vtCorrMid.Normalize()) { // assegno il versore correzione precedente vtCorrMid = vtCorrPrec ; // se possibile, cerco di migliorarlo considerando i rif. prec. e sul medio da direzioni fresa (Z) e ausiliaria (X) Frame3d refPrec, refMid ; if ( refPrec.Set( ORIG, vtDirPrec, vtAuxPrec) && refMid.Set( ORIG, vtDirMid, vtAuxMid)) { // versore correzione in locale al precente coincide con quello in locale al corrente vtCorrMid.ToLoc( refPrec) ; vtCorrMid.ToGlob( refMid) ; } } // inserisco nuova entità punto (per evitare problemi di lunghezza linea) // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( ptMid) ; // inserisco l'oggetto nel DB geometrico int nMidEntId = m_pGeomDB->InsertGeoObj( GDB_ID_NULL, nEntId, GDB_BEFORE, Release( pGP)) ; if ( nMidEntId == GDB_ID_NULL) return false ; // creo oggetto dati Cam const CamData* pEntCam = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pEntCam == nullptr) return false ; PtrOwner pCam( pEntCam->Clone()) ; if ( IsNull( pCam)) return false ; // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nMidEntId, Release( pCam)) ; // assegno valori CamData* pMidCamData = GetCamData( m_pGeomDB->GetUserObj( nMidEntId)) ; if ( pMidCamData == nullptr) return false ; pMidCamData->SetEndPoint( ptMid) ; pMidCamData->SetToolDir( vtDirMid) ; pMidCamData->SetAuxDir( vtAuxMid) ; pMidCamData->SetCorrDir( vtCorrMid) ; pMidCamData->SetFlag2( -1) ; pMidCamData->SetToolShow( bToolShow && nCnt < 4) ; // calcolo gli assi per il punto medio DBLVECTOR vAxRotHome( vAxMid.size()) ; DBLVECTOR vAxRotPrec( vAxMid) ; if ( ! CalculateRobotAxesValues( false, ptMid, vtDirMid, vtAuxMid, INFINITO, vAxRotHome, vAxRotPrec, vAxMid)) { pMidCamData->SetAxes( CamData::AS_ERR, vAxMid) ; return false ; } // verifico di essere nelle corse degli assi int nStat ; bool bOsOk = pMachine->VerifyOutstroke( 0, 0, 0, vAxMid, false, nStat) ; if ( ! bOsOk || nStat != 0) { pMidCamData->SetAxes( CamData::AS_OUTSTROKE, vAxMid) ; bAxError = true ; return true ; } // assegno i valori degli assi pMidCamData->SetAxes( CamData::AS_OK, vAxMid) ; // l'asse ausiliario di testa riportato sul grezzo coincide con quello già impostato pMidCamData->SetBackAuxDir( vtAuxMid) ; // devo verificare le due nuove parti ++ nCnt ; bool bAdded1, bAxError1, bAdded2, bAxError2 ; if ( ! VerifyRobotLineMidPoint( ptPrec, vtDirPrec, vtAuxPrec, vtCorrPrec, vAxPrec, ptMid, vtDirMid, vtAuxMid, vtCorrMid, vAxMid, nCnt, nMidEntId, nMoveType, bToolShow, bAdded1, bAxError1)) return false ; if ( bAxError1) bAxError = true ; if ( ! VerifyRobotLineMidPoint( ptMid, vtDirMid, vtAuxMid, vtCorrMid, vAxMid, ptP, vtDir, vtAux, vtCorr, vAxVal, nCnt, nEntId, nMoveType, bToolShow, bAdded2, bAxError2)) return false ; if ( bAxError2) bAxError = true ; return true ; } //---------------------------------------------------------------------------- bool Operation::AdjustStartEndMovements( bool bVerifyPreviousLink) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr || m_pMchMgr->GetCurrMachine() == nullptr) return false ; // se operazione vuota, non devo fare alcunché if ( IsEmpty( NEED_ONE_TP_OK)) return true ; // se nuova gestione collegamenti tra lavorazioni (sempre per i robot) if ( m_pMchMgr->GetCurrMachine()->GetNewLinkMgr()) return AdjustStartEndMovementsNew() ; // altrimenti modalità standard else return AdjustStartEndMovementsStd( bVerifyPreviousLink) ; } //---------------------------------------------------------------------------- bool Operation::AdjustStartEndMovementsStd( bool bVerifyPreviousLink) { // recupero ultima operazione precedente non vuota o che richiede home precedente // (disposizione qui considerata non vuota anche se nella path contiene solo comandi ausiliari) int nPrevOpId = m_pMchMgr->GetPrevActiveOperation( m_nOwnerId) ; Operation* pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; while ( pPrevOp != nullptr) { if ( ! pPrevOp->IsEmpty( NEED_ONE_TP_OK) || pPrevOp->NeedPrevHome()) break ; else { nPrevOpId = m_pMchMgr->GetPrevActiveOperation( nPrevOpId) ; pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; } } // recupero l'utensile precedente e i dati della sua testa string sPrevTool ; if ( pPrevOp != nullptr && ! pPrevOp->IsEmpty( NEED_ONE_TP_OK)) sPrevTool = pPrevOp->GetToolName() ; // determino posizione precedente assi DBLVECTOR vAxVal ; bool bMaxZ = false ; double dPrevOffsX = 0 ; // *** Se primo utensile o richiesto, uso la posizione home if ( sPrevTool.empty() || NeedPrevHome()) { // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // recupero posizione home if ( ! m_pMchMgr->GetAllCurrAxesHomePos( vAxVal)) return false ; // si parte da Z massima bMaxZ = true ; } // *** Se utensile non cambiato o su diversa uscita della stessa testa, uso posizione finale della lavorazione precedente else if ( ! ToolChangeNeeded( *pPrevOp, true, *this, true)) { // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // se richiesta verifica collegamento con lavorazione precedente if ( bVerifyPreviousLink) { // cancello eventuale risalita finale lavorazione precedente pPrevOp->RemoveRise() ; // recupero posizione finale lavorazione precedente if ( ! pPrevOp->GetFinalAxesValues( false, true, vAxVal)) return false ; // determinazione eventuale offset asse X (L1) al cambio di fase if ( ! SpecialPrevMachiningOffset( pPrevOp, dPrevOffsX)) return false ; // elimino eventuali CLIMB già presenti RemoveClimb() ; // recupero posizione iniziale lavorazione DBLVECTOR vAxIni ; if ( ! GetInitialAxesValues( false, true, vAxIni)) return false ; // recupero se ZHome è in basso bool bZHomeDown = GetZHomeDown() ; // recupero quote home e max di Z double dHomeZ ; if ( ! m_pMchMgr->GetCurrAxisHomePos( 2, dHomeZ)) return false ; // se necessaria, aggiungo risalita parziale double dDeltaZ = ( vAxIni[2] - vAxVal[2]) ; if ( ( ! bZHomeDown && dDeltaZ > 100 * EPS_SMALL && vAxIni[2] <= dHomeZ) || ( bZHomeDown && dDeltaZ < -100 * EPS_SMALL && vAxIni[2] >= dHomeZ)) { if ( ! pPrevOp->AddRise( vAxVal, dDeltaZ)) return false ; // aggiorno quota in Z della posizione iniziale ( anche se verrà aggiornata solo in seguito) vAxIni[2] = vAxVal[2] ; } // Verifico non ci sia collisione a HomeZ bool bToMaxZ = false ; bool bToMyHomeZ = false ; DBLVECTOR vAxVal2 = vAxVal ; vAxVal2[2] = dHomeZ ; DBLVECTOR vAxIni2 = vAxIni ; vAxIni2[2] = dHomeZ ; if ( ! TestCollisionAvoid( vAxVal2, dPrevOffsX, vAxIni2)) { // se ammessa risalita aggiuntiva Vector3d vtTprev ; pPrevOp->GetFinalToolDir( false, vtTprev) ; Vector3d vtTcurr ; GetInitialToolDir( false, vtTcurr) ; double dExtraZ ; if ( GetExtraZ( vAxVal2, vtTprev, vAxIni2, vtTcurr, dHomeZ, dExtraZ)) { if ( abs( dExtraZ) > EPS_SMALL) { double dMyHomeZ = dHomeZ + dExtraZ ; vAxVal2[2] = dMyHomeZ ; vAxIni2[2] = dMyHomeZ ; if ( TestCollisionAvoid( vAxVal2, dPrevOffsX, vAxIni2)) { dHomeZ = dMyHomeZ ; bToMyHomeZ = true ; } else bToMaxZ = true ; } else bToMaxZ = true ; } // se altrimenti ammessa rotazione a Zmax e richiesta rotazione else if ( GetRotationAtZmax() && (( vAxVal2.size() >= 4 && abs( vAxVal2[3] - vAxIni2[3]) > 5) || ( vAxVal2.size() >= 5 && abs( vAxVal2[4] - vAxIni2[4]) > 5))) { // verifico se va bene rotazione assi iniziale e poi movimento DBLVECTOR vAxMid2 = vAxVal2 ; vAxMid2[3] = vAxIni2[3] ; if ( vAxVal2.size() >= 5) vAxMid2[4] = vAxIni2[4] ; if ( TestCollisionAvoid( vAxVal2, dPrevOffsX, vAxMid2) && TestCollisionAvoid( vAxMid2, dPrevOffsX, vAxIni2) && pPrevOp->AddRise( vAxVal2) && pPrevOp->AddSpecialRise( vAxMid2, true, GDB_ID_NULL, true, 5)) { vAxVal = vAxMid2 ; bMaxZ = true ; } else return false ; } // altrimenti impossibile else return false ; } // Recupero box complessivo dei grezzi attivi BBox3d b3Raws ; GetCurrRawsGlobBox( b3Raws) ; // Se già a Zmax if ( bMaxZ) { // non devo fare alcunché } // se altrimenti richiesta risalita a Zmax else if ( bToMaxZ || ForcedZmax( vAxVal, vAxIni, b3Raws)) { // cancello eventuale risalita parziale della lavorazione precedente pPrevOp->RemoveRise() ; // aggiungo risalita a HomeZ/MaxZ if ( ! pPrevOp->AddRise( vAxVal, NAN, GDB_ID_NULL, true, 0)) return false ; // si parte da Z massima bMaxZ = true ; } // altrimenti, verifico se la testa interferisce con i pezzi o i bloccaggi sulla tavola else if ( bToMyHomeZ || ! TestCollisionAvoid( vAxVal, dPrevOffsX, vAxIni)) { // riprovo con risalita parziale bool bPartRise = false ; double dSafeZ = dHomeZ ; double dUnsafeZ = ( bZHomeDown ? min( vAxVal[2], vAxIni[2]) : max( vAxVal[2], vAxIni[2])) ; if ( ( ! bZHomeDown && dUnsafeZ < dSafeZ) || ( bZHomeDown && dUnsafeZ > dSafeZ)) { DBLVECTOR vAxVal2 = vAxVal ; DBLVECTOR vAxIni2 = vAxIni ; vAxVal2[2] = dUnsafeZ ; vAxIni2[2] = dUnsafeZ ; if ( TestCollisionAvoid( vAxVal2, dPrevOffsX, vAxIni2)) { bPartRise = true ; dSafeZ = dUnsafeZ ; } else { for ( int i = 1 ; i <= 3 ; ++ i) { double dTestSafeZ = ( dUnsafeZ + dSafeZ) / 2 ; vAxVal2[2] = dTestSafeZ ; vAxIni2[2] = dTestSafeZ ; if ( TestCollisionAvoid( vAxVal2, dPrevOffsX, vAxIni2)) { bPartRise = true ; dSafeZ = dTestSafeZ ; } else { dUnsafeZ = dTestSafeZ ; } } } // se necessario, riprovo appena prima di Zmax if ( ! bPartRise) { double dOffsZ = 1 * ( bZHomeDown ? 1 : -1) ; vAxVal2[2] = dHomeZ + dOffsZ ; vAxIni2[2] = dHomeZ + dOffsZ ; if ( TestCollisionAvoid( vAxVal2, dPrevOffsX, vAxIni2)) { bPartRise = true ; dSafeZ = dHomeZ + dOffsZ ; } } } if ( ! bPartRise) { // cancello eventuale risalita parziale della lavorazione precedente pPrevOp->RemoveRise() ; // aggiungo risalita a Zmax if ( ! pPrevOp->AddRise( vAxVal)) return false ; // si parte da Z massima bMaxZ = true ; } else { // cancello eventuale risalita parziale della lavorazione precedente pPrevOp->RemoveRise() ; // recupero le nuove quote finali (per calcolare il corretto delta) pPrevOp->GetFinalAxesValues( false, true, vAxVal) ; // aggiungo risalita a Zsafe double dDelta = ( ! bZHomeDown ? max( dSafeZ - vAxVal[2], 0.) : min( dSafeZ - vAxVal[2], 0.)) ; if ( ! pPrevOp->AddRise( vAxVal, dDelta)) return false ; // aggiorno quota iniziale in Z vAxIni[2] = dSafeZ ; } } // se ci sono aree protette... if ( m_pMchMgr->GetCurrMachine()->ExistProtectedAreas()) { // verifico se il collegamento le attraversa const double PA_VERIF_LEN = 10 ; Point3d ptStart( vAxVal[0], vAxVal[1], vAxVal[2]) ; Point3d ptEnd( vAxIni[0], vAxIni[1], vAxIni[2]) ; double dLen = DistXY( ptStart, ptEnd) ; int nStep = int( dLen / PA_VERIF_LEN + 1) ; bool bOutstroke = false ; for ( int i = 1 ; i < nStep ; ++ i) { Point3d ptCurr = Media( ptStart, ptEnd, double( i) / nStep) ; int nStat = 0 ; if ( ! m_pMchMgr->GetCurrMachine()->VerifyProtectedAreas( ptCurr.x, ptCurr.y, ptCurr.z, {}, nStat) || nStat != 0) { bOutstroke = true ; break ; } } // in caso di attraversamento dell'area proibita if ( bOutstroke) { // chiamo funzione script OnSpecialRapidMove per avere nuovo punto intermedio DBLVECTOR vAxNew ; bool bModif = false ; bool bOk = ( SpecialMoveRapid( vAxVal, vAxIni, vAxNew, bModif) && bModif) ; // inserisco il nuovo punto alla fine della lavorazione precedente if ( bOk) vAxVal = vAxNew ; if ( ! pPrevOp->AddSpecialRise( vAxVal, bOk)) return false ; } } } // altrimenti non richiesta verifica collegamento con lavorazione precedente else { // recupero posizione finale lavorazione precedente if ( ! pPrevOp->GetFinalAxesValues( false, true, vAxVal)) return false ; } } // *** Altrimenti aggiungo uscita per cambio utensile alla lavorazione precedente e parto da questa else { // imposto la lavorazione precedente come corrente m_pMchMgr->SetCurrMachining( pPrevOp->GetOwner()) ; // imposto l'utensile precedente per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( pPrevOp->GetToolName(), pPrevOp->GetHeadName(), pPrevOp->GetExitNbr())) return false ; // ricalcolo risalita di lavorazione precedente (se già calcolata) if ( pPrevOp->RemoveRise()) { if ( ! pPrevOp->AddRise( vAxVal)) return false ; } // ripristino la lavorazione corrente m_pMchMgr->SetCurrMachining( GetOwner()) ; // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // recupero posizione home if ( ! m_pMchMgr->GetAllCurrAxesHomePos( vAxVal)) return false ; // si parte da Z massima bMaxZ = true ; } // aggiusto l'inizio di ogni percorso di lavoro bool bOk = true ; int nPrevClPathId = GDB_ID_NULL ; int nClPathId = GetFirstFullToolpath() ; while ( bOk && nClPathId != GDB_ID_NULL) { // se richiesta verifica collegamento con lavorazione precedente, sistemo inizio if ( bVerifyPreviousLink) { if ( ! AdjustOneStartEndMovement( nClPathId, nPrevClPathId, pPrevOp, vAxVal, dPrevOffsX, bMaxZ)) bOk = false ; } bMaxZ = false ; dPrevOffsX = 0 ; // recupero nuovi finali RemoveRise( nClPathId) ; if ( ! GetClPathFinalAxesValues( nClPathId, false, vAxVal)) bOk = false ; // passo al successivo nPrevClPathId = nClPathId ; nClPathId = GetNextFullToolpath( nClPathId) ; pPrevOp = nullptr ; } // Aggiungo risalita finale bOk = bOk && AddRise( vAxVal) ; // se ultima operazione o la successiva lo richiede, vado in home int nNextOpId = m_pMchMgr->GetNextActiveOperation( m_nOwnerId) ; Operation* pNextOp = GetOperation( m_pGeomDB->GetUserObj( nNextOpId)) ; if ( pNextOp == nullptr || pNextOp->NeedPrevHome() || ( IsValidMachiningType( pNextOp->GetType()) && ! pNextOp->IsAtLeastOnePathOk())) bOk = bOk && AddHome() ; return bOk ; } //---------------------------------------------------------------------------- bool Operation::MoveHeadFromHomeToMach( bool bCurrMain, const string& sToolName, const string& sHeadName, int nExitNbr, int nStartZMax, int nOtherLinkType) { // verifico gestore delle lavorazioni e operazione if ( m_pMchMgr == nullptr) return false ; // imposto l'utensile corrente if ( ! m_pMchMgr->SetCalcTool( sToolName, sHeadName, nExitNbr)) return false ; // sistemo approccio a Zmax DBLVECTOR vAxVal ; if ( ! AdjustOneStartEndMovement( GetFirstFullToolpath( bCurrMain), GDB_ID_NULL, nullptr, vAxVal, 0, true)) return false ; // eseguo collegamento speciale if ( ! ManageSpecialLink( nullptr, GDB_ID_NULL, false, this, GDB_ID_NULL, bCurrMain, nStartZMax, nOtherLinkType)) return false ; // riporto l'utensile corrente come originale if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; return true ; } //---------------------------------------------------------------------------- bool Operation::MoveHeadFromMachToMach( Operation* pPrevOpe, bool bPrevMain, const string& sPrevToolName, const string& sPrevHeadName, int nPrevExitNbr, bool bCurrMain, const string& sCurrToolName, const string& sCurrHeadName, int nCurrExitNbr, bool bToolChangeNeeded, int nStartZMax, int nOtherLinkType, int nOtherLinkType1) { // verifico gestore delle lavorazioni e operazione precedente if ( m_pMchMgr == nullptr || pPrevOpe == nullptr) return false ; // se utensile non cambiato o su diversa uscita della stessa testa, uso posizione finale della lavorazione precedente if ( ! bToolChangeNeeded) { // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( sCurrToolName, sCurrHeadName, nCurrExitNbr)) return false ; // cancello eventuale risalita finale lavorazione precedente pPrevOpe->RemoveRise( GDB_ID_NULL, bPrevMain) ; // recupero posizione finale lavorazione precedente DBLVECTOR vAxVal ; if ( ! pPrevOpe->GetFinalAxesValues( false, bPrevMain, vAxVal)) return false ; // determinazione eventuale offset asse X (L1) al cambio di fase double dPrevOffsX = 0 ; if ( ! SpecialPrevMachiningOffset( pPrevOpe, dPrevOffsX)) return false ; // sistemo collegamento con lavorazione precedente ( senza passare per Zmax) if ( ! AdjustOneStartEndMovement( GetFirstFullToolpath( bCurrMain), GDB_ID_NULL, pPrevOpe, vAxVal, dPrevOffsX, false)) return false ; // gestione collegamento speciale if ( ! ManageSpecialLink( pPrevOpe, GDB_ID_NULL, bPrevMain, this, GDB_ID_NULL, bCurrMain, nStartZMax, nOtherLinkType)) return false ; } // se utensile diverso, agiungo uscita per camvio utensile alla lavorazione precedente e parto da questa else { // imposto la lavorazione precedente come corrente ed effettuo collegamento m_pMchMgr->SetCurrMachining( pPrevOpe->GetOwner()) ; if ( ! MoveHeadFromMachToHome( pPrevOpe, bPrevMain, sPrevToolName, sPrevHeadName, nPrevExitNbr, nStartZMax, nOtherLinkType)) return false ; // ripristino la lavorazione corrente ed effettuo collegamento m_pMchMgr->SetCurrMachining( GetOwner()) ; if ( ! MoveHeadFromHomeToMach( bCurrMain, sCurrToolName, sCurrHeadName, nCurrExitNbr, nStartZMax, nOtherLinkType1)) return false ; } // riporto l'utensile corrente come originale if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; return true ; } //---------------------------------------------------------------------------- bool Operation::MoveHeadFromMachToHome( Operation* pPrevOpe, bool bPrevMain, const string& sPrevToolName, const string& sPrevHeadName, int nPrevExitNbr, int nStartZMax, int nOtherLinkType) { // verifico gestore delle lavorazioni e operazione precedente if ( m_pMchMgr == nullptr || pPrevOpe == nullptr) return false ; // imposto l'utensile precedente per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( sPrevToolName, sPrevHeadName, nPrevExitNbr)) return false ; // ricalcolo risalita di lavorazione precedente (se già calcolata) if ( pPrevOpe->RemoveRise( GDB_ID_NULL, bPrevMain)) { DBLVECTOR vAxVal ; if ( ! pPrevOpe->AddRise( vAxVal, NAN, GDB_ID_NULL, bPrevMain)) return false ; } // eventuale rilascio utensile speciale di lavorazione precedente if ( ! pPrevOpe->ManageSpecialLink( pPrevOpe, GDB_ID_NULL, bPrevMain, nullptr, GDB_ID_NULL, false, nStartZMax, nOtherLinkType)) return false ; // riporto l'utensile corrente come originale if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; return true ; } //---------------------------------------------------------------------------- bool Operation::ManageDoubleOperNew( Operation* pPrevOpe) { // recupero i dati della lavorazione precedente string sPrevTool, sPrevTcPos, sPrevHead ; int nPrevExitNbr ; string sPrevDblTool, sPrevDblHead ; int nPrevDblExitNbr ; bool bPrevInDouble = false ; if ( pPrevOpe != nullptr && ! pPrevOpe->IsEmpty( NEED_ONE_TP_OK)) { sPrevTool = pPrevOpe->GetToolName() ; sPrevHead = pPrevOpe->GetHeadName() ; nPrevExitNbr = pPrevOpe->GetExitNbr() ; bPrevInDouble = ( pPrevOpe->GetFirstFullToolpath( false) != GDB_ID_NULL) ; } if ( bPrevInDouble) { if ( ! pPrevOpe->GetDoubleToolData( sPrevDblTool, sPrevTcPos, sPrevDblHead, nPrevDblExitNbr)) return false ; } // verifico se la lavorazione corrente è singola e in caso contrario recupero i dati della testa in doppio string sCurrDblTool, sCurrTcPos, sCurrDblHead ; int nCurrDblExitNbr ; bool bCurrInDouble = ( GetFirstFullToolpath( false) != GDB_ID_NULL) ; if ( bCurrInDouble) { if ( ! GetDoubleToolData( sCurrDblTool, sCurrTcPos, sCurrDblHead, nCurrDblExitNbr)) return false ; } // se nessuna delle due è un'Operazione in doppio, allora errore if ( ! bPrevInDouble && ! bCurrInDouble) return false ; // leggo da parametro della lavorazione corrente se risalita a ZMax forzata int nStartZMax = GetUserNotesZmax() ; // *** [A] Se primo utensile o richiesto, uso la posizione Home if ( sPrevTool.empty() || NeedPrevHome()) { // -->! la lavorazione corrente è per forza in doppio !<-- // Dalla sua posizione di Home porto la Testa Main sul percorso Main corrente if ( ! MoveHeadFromHomeToMach( true, GetToolName(), GetHeadName(), GetExitNbr(), nStartZMax, LINK_HOME_TO_MACH)) return false ; // Dalla sua posizione di Home porto da Testa Double sul percorso Double corrente if ( ! MoveHeadFromHomeToMach( false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, nStartZMax, LINK_HOME_TO_MACH)) return false ; } else { // *** [B] Se la lavorazione precedente è singola, allora la corrente in doppio if ( ! bPrevInDouble) { // -->! 1) Verifico se l'unico utensile della lavorazione precedente ( in quanto singola) è compatibile // con l'Utensile presente sul percorso Main della lavorazione corrente ( in doppio) !<-- if ( ! ToolChangeNeeded( *pPrevOpe, true, *this, true) && nStartZMax == 0) { // Come prima cosa sposto la testa Main della prima lavorazione sul percorso Main della lavorazione corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, true, GetToolName(), GetHeadName(), GetExitNbr(), false, nStartZMax, LINK_HOME_TO_MACH, LINK_NULL)) return false ; // Successivamente porto la testa Double dalla sua Home alla lavorazione corrente ( in doppio) sul percorso if ( ! MoveHeadFromHomeToMach( false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, nStartZMax, LINK_MACH_TO_MACH)) return false ; } // -->! 2) Verifico se l'unico utensile della lavorazione precedente ( in quanto singola) è compatibile // con l'utensile presente sul percorso Double della lavorazione corrente ( in doppio) !<-- else if ( ! ToolChangeNeeded( *pPrevOpe, true, *this, false) && nStartZMax == 0) { // Come prima cosa sposto la testa Main della lavorazione precedente sul percorso Double della lavorazione corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, false, nStartZMax, LINK_HOME_TO_MACH, LINK_NULL)) return false ; // Successivamente porto la testa Main dalla sua Home alla lavorazione corrente ( in doppio) sul percorso if ( ! MoveHeadFromHomeToMach( true, GetToolName(), GetHeadName(), GetExitNbr(), nStartZMax, LINK_MACH_TO_MACH)) return false ; } // -->! 3) In questo caso l'unico utensile della lavorazione precedente ( in quanto singola) non è compatibile // con nessun utensile della lavorazione corrente ( in doppio) !<-- else { // Porto la Testa Main sul percorso Main corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, true, GetToolName(), GetHeadName(), GetExitNbr(), true, nStartZMax, LINK_NULL, LINK_HOME_TO_MACH)) return false ; // Dalla sua posizione di Home porto da Testa Double sul percorso Double corrente if ( ! MoveHeadFromHomeToMach( false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, nStartZMax, LINK_HOME_TO_MACH)) return false ; } } // *** [C] se la lavorazione corrente è singola, allora la precedente è in doppio else if ( ! bCurrInDouble) { // -->! 1) Verifico se l'utensile principale della lavorazione precedente ( in doppio) è compatibile // con l'Utensile presente sul percorso Main della lavorazione corrente ( in quanto singola) !<-- if ( ! ToolChangeNeeded( *pPrevOpe, true, *this, true) && nStartZMax == 0) { // Come prima cosa sposto la testa Double della lavorazione precedente ( in doppio) in Home if ( ! MoveHeadFromMachToHome( pPrevOpe, false, sPrevDblTool, sPrevDblHead, nPrevDblExitNbr, nStartZMax, LINK_MACH_TO_MACH)) return false ; // Sposto al testa Main della lavorazione precedente sul percorso Main corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, true, GetToolName(), GetHeadName(), GetExitNbr(), false, nStartZMax, LINK_MACH_TO_HOME, LINK_NULL)) return false ; } // -->! 2) Verifico se l'utensile secondario della lavorazione precedente ( in doppio) è compatibile // con l'Utensile presente sul percorso Double della lavorazione corrente ( in quanto singola) !<-- else if ( ! ToolChangeNeeded( *pPrevOpe, false, *this, true) && nStartZMax == 0) { // Come prima cosa sposto la testa Main della lavorazione precedente ( in doppio) in Home if ( ! MoveHeadFromMachToHome( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, nStartZMax, LINK_MACH_TO_MACH)) return false ; // Sposto al testa Double della lavorazione precedente sul percorso corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, false, sPrevDblTool, sPrevDblHead, nPrevDblExitNbr, true, GetToolName(), GetHeadName(), GetExitNbr(), false, nStartZMax, LINK_MACH_TO_HOME, LINK_NULL)) return false ; } // -->! 3) In questo caso l'unico utensile della lavorazione corrente ( in quanto singola) non è compatibile // con nessun utensile della lavorazione precedente ( in doppio) !<-- else { // Come prima cosa sposto la testa Double della lavorazione precedente ( in doppio) in Home if ( ! MoveHeadFromMachToHome( pPrevOpe, false, sPrevDblTool, sPrevDblHead, nPrevDblExitNbr, nStartZMax, LINK_MACH_TO_HOME)) return false ; // Sposto la testa Main della lavorazione precedente sul percorso corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, true, GetToolName(), GetHeadName(), GetExitNbr(), true, nStartZMax, LINK_MACH_TO_HOME, LINK_NULL)) return false ; } } // *** [D] entrambe le lavorazioni sono in doppio else { // -->! 1) Entrambe le lavorazioni in doppio sono compatibili tra di loro !<-- if ( ! ToolChangeNeeded( *pPrevOpe, true, *this, true) && ! ToolChangeNeeded( *pPrevOpe, false, *this, false) && nStartZMax == 0) { // Porto la testa Main dalla lavorazione precedente alla lavorazione corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, true, GetToolName(), GetHeadName(), GetExitNbr(), false, nStartZMax, LINK_MACH_TO_MACH, LINK_NULL)) return false ; // Porto la testa double delle lavorazione precedente alla lavorazine corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, false, sPrevDblTool, sPrevDblHead, nPrevDblExitNbr, false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, false, nStartZMax, LINK_MACH_TO_MACH, LINK_NULL)) return false ; } // -->! 2) Per tutto il resto porto sempre in Home per sicurezza passando da una lavorazione all'altra !<-- else { // Porto la testa Main dalla lavorazione precedente alla lavorazione corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, true, sPrevTool, sPrevHead, nPrevExitNbr, true, GetToolName(), GetHeadName(), GetExitNbr(), true, nStartZMax, LINK_MACH_TO_HOME, LINK_HOME_TO_MACH)) return false ; // Porto la testa Double delle lavorazione precedente alla lavorazine corrente if ( ! MoveHeadFromMachToMach( pPrevOpe, false, sPrevDblTool, sPrevDblHead, nPrevDblExitNbr, false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, true, nStartZMax, LINK_MACH_TO_HOME, LINK_HOME_TO_MACH)) return false ; } } } // -->! [E] aggiusto il collegamento iniziale di ogni percoso di lavoro successivo al primo. Per ogni Path all'interno // del gruppo CL ( o DBL) corrente creo i link per questi percorsi. All'ultimo aggiungo aggiungo la Home !<-- bool bOk = true ; // 1) se lavorazione singola, allora tutti i Path sono singoli if ( ! bCurrInDouble) { int nPrevCLPathId = GetFirstFullToolpath() ; int nCLPathId = GetNextFullToolpath( nPrevCLPathId) ; while ( bOk && nCLPathId != GDB_ID_NULL) { // -->! recupero i nuovi finali senza risalite !<-- RemoveRise( nPrevCLPathId) ; DBLVECTOR vAxVal ; bOk = bOk && GetClPathFinalAxesValues( nPrevCLPathId, false, vAxVal) ; bOk = bOk && AdjustOneStartEndMovement( nCLPathId, nPrevCLPathId, nullptr, vAxVal, 0, false) ; bOk = bOk && ManageSpecialLink( this, nPrevCLPathId, true, this, nCLPathId, true, nStartZMax, LINK_NULL) ; // passo al successivo nPrevCLPathId = nCLPathId ; nCLPathId = GetNextFullToolpath( nCLPathId) ; pPrevOpe = nullptr ; } // -->! aggiungo risalita finale !<-- DBLVECTOR vAxVal ; bOk = bOk && AddRise( vAxVal) ; // -->! nuova verifica di aree protette alla fine !<-- bOk = bOk && ManageSpecialLink( this, GDB_ID_NULL, true, nullptr, GDB_ID_NULL, false, nStartZMax) ; // -->! se ultima operazione o la successiva lo richiede, vado in Home !<-- int nNextOpId = m_pMchMgr->GetNextActiveOperation( m_nOwnerId) ; Operation* pNextOp = GetOperation( m_pGeomDB->GetUserObj( nNextOpId)) ; if ( pNextOp == nullptr || pNextOp->NeedPrevHome() || ( IsValidMachiningType( pNextOp->GetType()) && ! pNextOp->IsAtLeastOnePathOk())) bOk = bOk && AddHome() ; } // 2) se lavorazione in doppio, allora scorro a coppie i Path nel CL e in DBL else { int nPrevCLPathId = GetFirstFullToolpath() ; int nPrevDBLPathId = GetFirstFullToolpath( false) ; int nCLPathId = GetNextFullToolpath( nPrevCLPathId) ; int nDBLPathId = GetNextFullToolpath( nPrevDBLPathId, false) ; while ( bOk && nCLPathId != GDB_ID_NULL && nDBLPathId != GDB_ID_NULL) { // -->! recupero i nuovi finali senza risalite !<-- RemoveRise( nCLPathId) ; RemoveRise( nDBLPathId, false) ; DBLVECTOR vAxVal ; // Utensile Main bOk = bOk && GetClPathFinalAxesValues( nPrevCLPathId, false, vAxVal) ; bOk = bOk && AdjustOneStartEndMovement( nCLPathId, nPrevCLPathId, nullptr, vAxVal, 0, false) ; bOk = bOk && ManageSpecialLink( this, nPrevCLPathId, true, this, nCLPathId, true, nStartZMax, LINK_MACH_TO_HOME) ; // Utensile Double DBLVECTOR vAxDblVal ; bOk = bOk && m_pMchMgr->SetCalcTool( sCurrDblTool, sCurrDblHead, nCurrDblExitNbr) ; bOk = bOk && GetClPathFinalAxesValues( nPrevDBLPathId, false, vAxDblVal) ; bOk = bOk && AdjustOneStartEndMovement( nDBLPathId, nPrevDBLPathId, nullptr, vAxDblVal, 0, false) ; bOk = bOk && ManageSpecialLink( this, nPrevDBLPathId, false, this, nDBLPathId, false, nStartZMax, LINK_MACH_TO_HOME) ; bOk = bOk && m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr()) ; // passo al successivo nPrevCLPathId = nCLPathId ; nPrevDBLPathId = nDBLPathId ; nCLPathId = GetNextFullToolpath( nCLPathId) ; nDBLPathId = GetNextFullToolpath( nDBLPathId, false) ; pPrevOpe = nullptr ; } // -->! aggiungo risalita finale ed eseguo collegamento speciale !<-- // per utensile Main DBLVECTOR vAxVal ; bOk = bOk && AddRise( vAxVal) ; bOk = bOk && ManageSpecialLink( this, GDB_ID_NULL, true, nullptr, GDB_ID_NULL, true, 0, LINK_NULL) ; // per utensile Double DBLVECTOR vAxDblVal ; bOk = bOk && m_pMchMgr->SetCalcTool( sCurrDblTool, sCurrDblHead, nCurrDblExitNbr) ; bOk = bOk && AddRise( vAxDblVal, NAN, GDB_ID_NULL, false) ; bOk = bOk && ManageSpecialLink( this, GDB_ID_NULL, false, nullptr, GDB_ID_NULL, false, 0, LINK_NULL) ; bOk = bOk && m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr()) ; // -->! se ultima operazione o la successiva lo richiede, vado in Home !<-- int nNextOpId = m_pMchMgr->GetNextActiveOperation( m_nOwnerId) ; Operation* pNextOp = GetOperation( m_pGeomDB->GetUserObj( nNextOpId)) ; if ( pNextOp == nullptr || pNextOp->NeedPrevHome() || ( IsValidMachiningType( pNextOp->GetType()) && ! pNextOp->IsAtLeastOnePathOk())) { // per utensile Main bOk = bOk && AddHome() ; // per utensile Double bOk = bOk && m_pMchMgr->SetCalcTool( sCurrDblTool, sCurrDblHead, nCurrDblExitNbr) ; bOk = bOk && AddHome( false) ; bOk = bOk && m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr()) ; } } return bOk ; } //---------------------------------------------------------------------------- bool Operation::AdjustStartEndMovementsNew( void) { // recupero ultima operazione precedente non vuota o che richiede home precedente // (disposizione qui considerata non vuota anche se nella path contiene solo comandi ausiliari) int nPrevOpId = m_pMchMgr->GetPrevActiveOperation( m_nOwnerId) ; Operation* pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; while ( pPrevOp != nullptr) { if ( ! pPrevOp->IsEmpty( NEED_ONE_TP_OK) || pPrevOp->NeedPrevHome()) break ; else { nPrevOpId = m_pMchMgr->GetPrevActiveOperation( nPrevOpId) ; pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; } } // recupero l'utensile precedente string sPrevTool ; bool bPrevDbl = false ; if ( pPrevOp != nullptr && ! pPrevOp->IsEmpty( NEED_ONE_TP_OK)) { sPrevTool = pPrevOp->GetToolName() ; bPrevDbl = ( pPrevOp->GetFirstFullToolpath( false) != GDB_ID_NULL) ; } // verifico se la lavorazione corrente è in doppio bool bCurrDbl = ( GetFirstFullToolpath( false) != GDB_ID_NULL) ; // -->! se entrambe le lavorazioni sono singole !<-- if ( ! bPrevDbl && ! bCurrDbl) { // leggo da parametro della lavorazione corrente se risalita a ZMax forzata int nStartZMax = GetUserNotesZmax() ; // *** Se primo utensile o richiesto, uso la posizione home if ( sPrevTool.empty() || NeedPrevHome()) { // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // sistemo approccio da Zmax DBLVECTOR vAxVal ; if ( ! AdjustOneStartEndMovement( GetFirstFullToolpath(), GDB_ID_NULL, nullptr, vAxVal, 0, true)) return false ; // eseguo collegamento speciale if ( ! ManageSpecialLink( nullptr, GDB_ID_NULL, true, this, GDB_ID_NULL, true, nStartZMax)) return false ; } // *** Se utensile non cambiato o su diversa uscita della stessa testa, uso posizione finale della lavorazione precedente else if ( ! ToolChangeNeeded( *pPrevOp, true, *this, true) && nStartZMax == 0) { // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // cancello eventuale risalita finale lavorazione precedente pPrevOp->RemoveRise() ; // recupero posizione finale lavorazione precedente DBLVECTOR vAxVal ; if ( ! pPrevOp->GetFinalAxesValues( false, true, vAxVal)) return false ; // determinazione eventuale offset asse X (L1) al cambio di fase double dPrevOffsX = 0 ; if ( ! SpecialPrevMachiningOffset( pPrevOp, dPrevOffsX)) return false ; // sistemo collegamento con lavorazione precedente if ( ! AdjustOneStartEndMovement( GetFirstFullToolpath(), GDB_ID_NULL, pPrevOp, vAxVal, dPrevOffsX, false)) return false ; // gestione collegamento speciale if ( ! ManageSpecialLink( pPrevOp, GDB_ID_NULL, true, this, GDB_ID_NULL, true, nStartZMax)) return false ; } // *** Altrimenti aggiungo uscita per cambio utensile alla lavorazione precedente e parto da questa else { // imposto la lavorazione precedente come corrente m_pMchMgr->SetCurrMachining( pPrevOp->GetOwner()) ; // imposto l'utensile precedente per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( pPrevOp->GetToolName(), pPrevOp->GetHeadName(), pPrevOp->GetExitNbr())) return false ; // ricalcolo risalita di lavorazione precedente (se già calcolata) if ( pPrevOp->RemoveRise()) { DBLVECTOR vAxVal ; if ( ! pPrevOp->AddRise( vAxVal)) return false ; } // eventuale rilascio utensile speciale di lavorazione precedente if ( ! pPrevOp->ManageSpecialLink( pPrevOp, GDB_ID_NULL, true, nullptr, GDB_ID_NULL, true, nStartZMax)) return false ; // ripristino la lavorazione corrente m_pMchMgr->SetCurrMachining( GetOwner()) ; // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // sistemo approccio da Zmax DBLVECTOR vAxVal ; if ( ! AdjustOneStartEndMovement( GetFirstFullToolpath(), GDB_ID_NULL, nullptr, vAxVal, 0, true)) return false ; // eseguo verifica if ( ! ManageSpecialLink( nullptr, GDB_ID_NULL, true, this, GDB_ID_NULL, true, nStartZMax)) return false ; } // aggiusto il collegamento iniziale di ogni percorso di lavoro successivo al primo bool bOk = true ; int nPrevClPathId = GetFirstFullToolpath() ; int nClPathId = GetNextFullToolpath( nPrevClPathId) ; while ( bOk && nClPathId != GDB_ID_NULL) { // recupero nuovi finali senza risalite RemoveRise( nPrevClPathId) ; DBLVECTOR vAxVal ; if ( ! GetClPathFinalAxesValues( nPrevClPathId, false, vAxVal)) bOk = false ; // sistemo collegamento con precedente if ( ! AdjustOneStartEndMovement( nClPathId, nPrevClPathId, nullptr, vAxVal, 0, false)) bOk = false ; // gestione collegamento speciale ( tra due percorsi della stessa lavorazione) if ( ! ManageSpecialLink( this, nPrevClPathId, true, this, nClPathId, true, nStartZMax)) return false ; // passo al successivo nPrevClPathId = nClPathId ; nClPathId = GetNextFullToolpath( nClPathId) ; pPrevOp = nullptr ; } // Aggiungo risalita finale DBLVECTOR vAxVal ; bOk = bOk && AddRise( vAxVal) ; // eseguo collegamento speciale if ( ! ManageSpecialLink( this, GDB_ID_NULL, true, nullptr, GDB_ID_NULL, true, 0)) return false ; // se ultima operazione o la successiva lo richiede, vado in home int nNextOpId = m_pMchMgr->GetNextActiveOperation( m_nOwnerId) ; Operation* pNextOp = GetOperation( m_pGeomDB->GetUserObj( nNextOpId)) ; if ( pNextOp == nullptr || pNextOp->NeedPrevHome() || ( IsValidMachiningType( pNextOp->GetType()) && ! pNextOp->IsAtLeastOnePathOk())) bOk = bOk && AddHome() ; return bOk ; } // -->! se presenza di una lavorazione in doppio !<-- else return ( ManageDoubleOperNew( pPrevOp)) ; } //---------------------------------------------------------------------------- bool Operation::ManageSpecialLink( Operation* pPrevOpe, int nPrevClPathId, bool bPrevMain, Operation* pNextOpe, int nNextClPathId, bool bNextMain, int nStartZmax, int nOtherLinkType) { // controlli if ( pPrevOpe == nullptr && pNextOpe == nullptr) return false ; // codifica tipo di collegamento ( 1, 2 o 3) int nLinkType = ( pPrevOpe != nullptr ? 2 : 0) + ( pNextOpe != nullptr ? 1 : 0) ; // assegno valori assi iniziali e finali, depurati di approcci/retrazioni aggiuntive (CLIMB e RISE) in area protetta DBLVECTOR vAxSta, vNeatAxSta ; if ( pPrevOpe != nullptr) { if ( nPrevClPathId == GDB_ID_NULL) { pPrevOpe->GetFinalAxesValues( false, bPrevMain, vAxSta) ; pPrevOpe->GetFinalAxesValues( true, bPrevMain, vNeatAxSta) ; } else { pPrevOpe->GetClPathFinalAxesValues( nPrevClPathId, false, vAxSta) ; // dall'Id ricavo già se main o double pPrevOpe->GetClPathFinalAxesValues( nPrevClPathId, true, vNeatAxSta) ; // dall'Id ricavo già se main o double } } else m_pMchMgr->GetAllCurrAxesHomePos( vNeatAxSta) ; DBLVECTOR vAxEnd, vNeatAxEnd ; if ( pNextOpe != nullptr) { if ( nNextClPathId == GDB_ID_NULL) { pNextOpe->GetInitialAxesValues( false, bNextMain, vAxEnd) ; pNextOpe->GetInitialAxesValues( true, bNextMain, vNeatAxEnd) ; } else { pNextOpe->GetClPathInitialAxesValues( nNextClPathId, false, vAxEnd) ; // dall'Id ricavo già se main o double pNextOpe->GetClPathInitialAxesValues( nNextClPathId, true, vNeatAxEnd) ; // dall'Id ricavo già se main o double } } else m_pMchMgr->GetAllCurrAxesHomePos( vNeatAxEnd) ; // verifico coerenza assi if ( ssize( vNeatAxSta) != ssize( vNeatAxEnd)) return false ; // chiamo funzione script OnSpecialRapidMove per avere posizioni di movimento bool bOk = SpecialLink( vNeatAxSta, vNeatAxEnd, nLinkType, pPrevOpe, bPrevMain, pNextOpe, bNextMain, nStartZmax, nOtherLinkType) ; // in caso di errore eseguo inserimento con segnalazione di errore if ( ! bOk) { if ( pPrevOpe != nullptr) pPrevOpe->AddSpecialRise( vAxSta, false) ; if ( pNextOpe != nullptr) pNextOpe->AddSpecialClimb( vAxEnd, false) ; return false ; } return true ; } //---------------------------------------------------------------------------- bool Operation::AdjustOneStartEndMovement( int nClPathId, int nPrevClPathId, Operation* pPrevOp, const DBLVECTOR& vAxPrev, double dPrevOffsX, bool bMaxZ) { // verifico gestore delle lavorazione, DB geometrico e macchina corrente if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; Machine* pMch = m_pMchMgr->GetCurrMachine() ; if ( pMch == nullptr) return false ; // dagli Id dei Path verifico se è Main o Double string sCLName ; m_pGeomDB->GetName( m_pGeomDB->GetParentId( nClPathId), sCLName) ; bool bMain = ( ! EqualNoCase( sCLName, MCH_DBL)) ; string sCLPrevName ; m_pGeomDB->GetName( m_pGeomDB->GetParentId( nPrevClPathId), sCLPrevName) ; bool bPrevMain = ( ! EqualNoCase( sCLPrevName, MCH_DBL)) ; // elimino eventuali CLIMB già presenti RemoveClimb( nClPathId, bMain) ; // recupero la prima entità di questo percorso int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // recupero i valori degli assi const DBLVECTOR vAxCurr = pCamData->GetAxesVal() ; // se provengo da Z massima if ( bMaxZ) { // se centro di lavoro if ( m_pMchMgr->GetCurrIsMcent()) { // copio l'entità int nNewEntId = m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_BEFORE) ; if ( nNewEntId == GDB_ID_NULL) return false ; // aggiorno il CamData pCamData = GetCamData( m_pGeomDB->GetUserObj( nNewEntId)) ; if ( pCamData == nullptr) return false ; // recupero HomeZ double dHomeZ ; if ( ! m_pMchMgr->GetCurrAxisHomePos( 2, dHomeZ)) return false ; // aggiungo eventuale ExtraZ const CamData* pCamDataPrev = GetClPathFinalCamData( nPrevClPathId, true) ; const DBLVECTOR& vAxPrev = ( pCamDataPrev != nullptr ? pCamDataPrev->GetAxesVal() : DBLVECTOR()) ; const Vector3d& vtTprev = ( pCamDataPrev != nullptr ? pCamDataPrev->GetToolDir() : V_NULL) ; const Vector3d& vtTcurr = pCamData->GetToolDir() ; double dExtraZ ; if ( GetExtraZ( vAxPrev, vtTprev, vAxCurr, vtTcurr, dHomeZ, dExtraZ)) { if ( abs( dExtraZ) > EPS_SMALL) dHomeZ += dExtraZ ; } // modifico quella copiata (è la prima del percorso) m_pGeomDB->SetName( nNewEntId, MCH_CL_CLIMB) ; m_pGeomDB->RemoveInfo( nNewEntId, KEY_CL_DOUBLE) ; DBLVECTOR vAxNew = vAxCurr ; vAxNew[2] = dHomeZ ; int nFlagNew = 2 ; int nFlag2New = 1 ; // eventuali aggiustamenti speciali dipendenti dalla macchina DBLVECTOR vAxTmp = vAxNew ; Vector3d vtTool = pCamData->GetToolDir() ; int nFlagTmp = nFlagNew ; int nFlag2Tmp = nFlag2New ; bool bModif ; if ( ! SpecialMoveZup( vAxTmp, vtTool, nFlagTmp, nFlag2Tmp, bModif)) return false ; if ( bModif) { vAxNew = vAxTmp ; pCamData->SetToolDir( vtTool) ; Vector3d vtAux = pCamData->GetAuxDir() ; if ( ! vtAux.IsSmall()) { DBLVECTOR vAng( vAxNew.begin() + 3, vAxNew.end()) ; if ( m_pMchMgr->GetCalcAuxDirFromAngles( vAng, vtAux)) pCamData->SetAuxDir( vtAux) ; } nFlagNew = nFlagTmp ; nFlag2New = nFlag2Tmp ; } // applico le modifiche (non forzo emissione della Z che è già massima) pCamData->SetAxes( CamData::AS_OK, vAxNew) ; pCamData->ChangeAxesMask( CamData::MSK_L1 | CamData::MSK_L2 | CamData::MSK_R1 | CamData::MSK_R2 | CamData::MSK_R3) ; pCamData->SetFlag( nFlagNew) ; pCamData->SetFlag2( nFlag2New) ; } // se altrimenti robot else if ( m_pMchMgr->GetCurrIsRobot()) { // inserisco la posizione iniziale a Zmax if ( ! AddRobotClimb( nEntId)) return false ; } } // verifico se la testa interferisce con i pezzi o i bloccaggi sulla tavola else { // se centro di lavoro if ( m_pMchMgr->GetCurrIsMcent()) { // Recupero box complessivo dei grezzi attivi BBox3d b3Raws ; GetCurrRawsGlobBox( b3Raws) ; // recupero se ZHome è in basso bool bZHomeDown = GetZHomeDown() ; // determino la Z più alta tra le due posizioni double dTopZ = ( bZHomeDown ? min( vAxPrev[2], vAxCurr[2]) : max( vAxPrev[2], vAxCurr[2])) ; // per il test uso posizioni temporanee con questa Z DBLVECTOR vAxPrevTmp = vAxPrev ; vAxPrevTmp[2] = dTopZ ; DBLVECTOR vAxCurrTmp = vAxCurr ; vAxCurrTmp[2] = dTopZ ; vAxPrevTmp[0] += dPrevOffsX ; // verifico se forzata risalita a Zmax bool bForcedZMax = ForcedZmax( vAxPrevTmp, vAxCurrTmp, b3Raws) ; // se interferisce int nLKAMO = LKAMO_INTERP ; if ( bForcedZMax || ! TestCollisionAvoid( vAxPrevTmp, 0, vAxCurrTmp, &nLKAMO)) { // recupero HomeZ double dHomeZ ; if ( ! m_pMchMgr->GetCurrAxisHomePos( 2, dHomeZ)) return false ; bool bPartRise = false ; double dSafeZ = dHomeZ ; // se non forzata Zmax if ( ! bForcedZMax) { // riprovo con una Z intermedia double dUnsafeZ = dTopZ ; for ( int i = 1 ; i <= 3 ; ++ i) { double dTestSafeZ = ( dUnsafeZ + dSafeZ) / 2 ; vAxPrevTmp[2] = dTestSafeZ ; vAxCurrTmp[2] = dTestSafeZ ; if ( TestCollisionAvoid( vAxPrevTmp, 0, vAxCurrTmp)) { bPartRise = true ; dSafeZ = dTestSafeZ ; } else dUnsafeZ = dTestSafeZ ; } // se necessario, riprovo appena prima di Zmax if ( ! bPartRise) { double dOffsZ = 5 * ( bZHomeDown ? 1 : -1) ; vAxPrevTmp[2] = dHomeZ + dOffsZ ; vAxCurrTmp[2] = dHomeZ + dOffsZ ; if ( TestCollisionAvoid( vAxPrevTmp, 0, vAxCurrTmp)) { bPartRise = true ; dSafeZ = dHomeZ + dOffsZ ; } } } // risalita sopra fine percorso precedente DBLVECTOR vAxNew1 ; if ( nPrevClPathId == GDB_ID_NULL) { if ( pPrevOp == nullptr || ! pPrevOp->RemoveRise( GDB_ID_NULL, bPrevMain)) return false ; DBLVECTOR vAxPrevNoRise ; pPrevOp->GetFinalAxesValues( false, bPrevMain, vAxPrevNoRise) ; double dDelta = ( ! bZHomeDown ? max( dSafeZ - vAxPrevNoRise[2], 0.) : min( dSafeZ - vAxPrevNoRise[2], 0.)) ; if ( ! pPrevOp->AddRise( vAxNew1, dDelta, GDB_ID_NULL, bPrevMain)) return false ; } else { double dDelta = ( ! bZHomeDown ? max( dSafeZ - vAxPrev[2], 0.) : min( dSafeZ - vAxPrev[2], 0.)) ; if ( ! AddRise( vAxNew1, dDelta, nPrevClPathId, bPrevMain)) return false ; } // aggiungo posizione elevata prima dell'inizio del percorso corrente int nNewEntId = m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_BEFORE) ; if ( nNewEntId == GDB_ID_NULL) return false ; // aggiorno il CamData pCamData = GetCamData( m_pGeomDB->GetUserObj( nNewEntId)) ; if ( pCamData == nullptr) return false ; // modifico l'entità copiata (è la prima del percorso) m_pGeomDB->SetName( nNewEntId, MCH_CL_CLIMB) ; m_pGeomDB->RemoveInfo( nNewEntId, KEY_CL_DOUBLE) ; DBLVECTOR vAxNew = vAxCurr ; vAxNew[2] = vAxNew1[2] ; int nFlagNew = 2 ; int nFlag2New = ( bPartRise ? 0 : 1) ; // eventuali aggiustamenti speciali dipendenti dalla macchina DBLVECTOR vAxTmp = vAxNew ; Vector3d vtTool = pCamData->GetToolDir() ; int nFlagTmp = nFlagNew ; int nFlag2Tmp = nFlag2New ; bool bModif ; if ( ! SpecialMoveZup( vAxTmp, vtTool, nFlagTmp, nFlag2Tmp, bModif)) return false ; if ( bModif) { vAxNew = vAxTmp ; pCamData->SetToolDir( vtTool) ; Vector3d vtAux = pCamData->GetAuxDir() ; if ( ! vtAux.IsSmall()) { DBLVECTOR vAng( vAxNew.begin() + 3, vAxNew.end()) ; if ( m_pMchMgr->GetCalcAuxDirFromAngles( vAng, vtAux)) pCamData->SetAuxDir( vtAux) ; } nFlagNew = nFlagTmp ; nFlag2New = nFlag2Tmp ; } // applico le modifiche (non forzo emissione della Z che è già alta) pCamData->SetAxes( CamData::AS_OK, vAxNew) ; pCamData->ChangeAxesMask( CamData::MSK_L1 | CamData::MSK_L2 | CamData::MSK_R1 | CamData::MSK_R2 | CamData::MSK_R3) ; pCamData->SetFlag( nFlagNew) ; pCamData->SetFlag2( nFlag2New) ; } // altrimenti else { // se Z pressochè uguali non devo fare alcunché if ( abs( vAxCurr[2] - vAxPrev[2]) <= 100 * EPS_SMALL) ; // se Z corrente maggiore della precedente o viceversa se HomeZ sotto else if ( ( ! bZHomeDown && vAxCurr[2] > vAxPrev[2]) || ( bZHomeDown && vAxCurr[2] < vAxPrev[2]) ) { // se vengo da ultimo percorso di precedente lavorazione if ( nPrevClPathId == GDB_ID_NULL) { // deve esistere lavorazione precedente if ( pPrevOp == nullptr) return false ; // se non presente risalita finale if ( m_pGeomDB->GetFirstNameInGroup( pPrevOp->GetLastFullToolpath( bPrevMain), MCH_CL_RISE) == GDB_ID_NULL) { // la aggiungo DBLVECTOR vAxNew ; double dDelta = ( ! bZHomeDown ? max( vAxCurr[2] - vAxPrev[2], 0.) : min( vAxCurr[2] - vAxPrev[2], 0.)) ; if ( ! pPrevOp->AddRise( vAxNew, dDelta, GDB_ID_NULL, bPrevMain)) return false ; } } // altrimenti vengo da precedente percorso della stessa lavorazione else { // aggiungo risalita alla fine del precedente percorso DBLVECTOR vAxNew ; double dDelta = ( ! bZHomeDown ? max( vAxCurr[2] - vAxPrev[2], 0.) : min( vAxCurr[2] - vAxPrev[2], 0.)) ; if ( ! AddRise( vAxNew, dDelta, nPrevClPathId, bPrevMain)) return false ; } } // altrimenti Z corrente minore della precedente else { // aggiungo posizione elevata prima dell'inizio del percorso corrente int nNewEntId = m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_BEFORE) ; if ( nNewEntId == GDB_ID_NULL) return false ; // aggiorno il CamData pCamData = GetCamData( m_pGeomDB->GetUserObj( nNewEntId)) ; if ( pCamData == nullptr) return false ; // modifico l'entità copiata (è la prima del percorso) m_pGeomDB->SetName( nNewEntId, MCH_CL_CLIMB) ; m_pGeomDB->RemoveInfo( nNewEntId, KEY_CL_DOUBLE) ; DBLVECTOR vAxNew = vAxCurr ; vAxNew[2] = vAxPrev[2] ; int nFlagNew = 2 ; int nFlag2New = 0 ; // eventuali aggiustamenti speciali dipendenti dalla macchina DBLVECTOR vAxTmp = vAxNew ; Vector3d vtTool = pCamData->GetToolDir() ; int nFlagTmp = nFlagNew ; int nFlag2Tmp = nFlag2New ; bool bModif ; if ( ! SpecialMoveZup( vAxTmp, vtTool, nFlagTmp, nFlag2Tmp, bModif)) return false ; if ( bModif) { vAxNew = vAxTmp ; pCamData->SetToolDir( vtTool) ; Vector3d vtAux = pCamData->GetAuxDir() ; if ( ! vtAux.IsSmall()) { DBLVECTOR vAng( vAxNew.begin() + 3, vAxNew.end()) ; if ( m_pMchMgr->GetCalcAuxDirFromAngles( vAng, vtAux)) pCamData->SetAuxDir( vtAux) ; } nFlagNew = nFlagTmp ; nFlag2New = nFlag2Tmp ; } // applico le modifiche (non forzo emissione della Z che è già alta) pCamData->SetAxes( CamData::AS_OK, vAxNew) ; pCamData->ChangeAxesMask( CamData::MSK_L1 | CamData::MSK_L2 | CamData::MSK_R1 | CamData::MSK_R2 | CamData::MSK_R3) ; pCamData->SetFlag( nFlagNew) ; pCamData->SetFlag2( nFlag2New) ; } // se necessario, aggiungo eventuale movimento orizzontale all'inizio della corrente if ( nLKAMO == LKAMO_HEAD_BEFORE || nLKAMO == LKAMO_HEAD_AFTER) { // aggiungo prima della corrente nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; int nNewEntId = m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_BEFORE) ; if ( nNewEntId == GDB_ID_NULL) return false ; // aggiorno il CamData pCamData = GetCamData( m_pGeomDB->GetUserObj( nNewEntId)) ; if ( pCamData == nullptr) return false ; // modifico l'entità copiata (è la prima del percorso) m_pGeomDB->SetName( nNewEntId, MCH_CL_CLIMB) ; m_pGeomDB->RemoveInfo( nNewEntId, KEY_CL_DOUBLE) ; DBLVECTOR vAxNew = pCamData->GetAxesVal() ; for ( int i = 0 ; i < 2 ; ++ i) { bool bLinear, bHead ; pMch->GetCurrAxisType( i, bLinear, bHead) ; if ( ( ! bHead && nLKAMO == LKAMO_HEAD_BEFORE) || ( bHead && nLKAMO == LKAMO_HEAD_AFTER)) vAxNew[i] = vAxPrev[i] ; } // applico le modifiche pCamData->SetAxes( CamData::AS_OK, vAxNew) ; pCamData->ChangeAxesMask( CamData::MSK_L1 | CamData::MSK_L2 | CamData::MSK_L3 | CamData::MSK_R1 | CamData::MSK_R2 | CamData::MSK_R3) ; pCamData->SetFlag( 0) ; pCamData->SetFlag2( 0) ; } return true ; } } // se robot else if ( m_pMchMgr->GetCurrIsRobot()) { // recupero dati posizione finale lavorazione precedente const CamData* pCamDataPrev = GetClPathFinalCamData( nPrevClPathId, true) ; if ( nPrevClPathId == GDB_ID_NULL && pPrevOp != nullptr) pCamDataPrev = pPrevOp->GetFinalCamData( true) ; if ( pCamDataPrev == nullptr) return false ; const DBLVECTOR& vAxPrev = pCamDataPrev->GetAxesVal() ; const Point3d& ptPEnd = pCamDataPrev->GetEndPoint() ; const Vector3d& vtDirEnd = pCamDataPrev->GetToolDir() ; const Vector3d& vtAuxEnd = pCamDataPrev->GetAuxDir() ; // recupero dati posizione iniziale lavorazione corrente // vAxCurr già recuperati sopra const Point3d& ptPStart = pCamData->GetEndPoint() ; const Vector3d& vtDirStart = pCamData->GetToolDir() ; const Vector3d& vtAuxStart = pCamData->GetAuxDir() ; // verifico se collegamento diretto tra le due posizioni (muovendosi alla Z maggiore) è possibile senza collisioni int nLowerPos = 0 ; DBLVECTOR vAxEnd = vAxPrev ; DBLVECTOR vAxStart = vAxCurr ; if ( ptPEnd.z < ptPStart.z - 100 * EPS_SMALL) { nLowerPos = - 1 ; if ( ! CalcRobotAxesAbovePos( ptPEnd, vtDirEnd, vtAuxEnd, ptPStart.z - ptPEnd.z, vAxEnd)) return false ; } else if ( ptPEnd.z > ptPStart.z + 100 * EPS_SMALL) { nLowerPos = 1 ; if ( ! CalcRobotAxesAbovePos( ptPStart, vtDirStart, vtAuxStart, ptPEnd.z - ptPStart.z, vAxStart)) return false ; } if ( TestCollisionAvoid( vAxEnd, 0, vAxStart)) { // se precedente più basso if ( nLowerPos == -1) { // se precedente appartiene ad altra operazione if ( nPrevClPathId == GDB_ID_NULL) { // elimino eventuali risalite finali if ( pPrevOp == nullptr || ! pPrevOp->RemoveRise()) return false ; // aggiungo risalita a Z opportuna alla fine del percorso precedente DBLVECTOR vAxNew = vAxPrev ; if ( ! pPrevOp->AddRise( vAxNew, ptPStart.z - ptPEnd.z, GDB_ID_NULL, bPrevMain)) return false ; } // altrimenti precedente appartiene alla stessa operazione else { // eventuali risalite finali già eliminate dal chiamante // aggiungo risalita a Z opportuna alla fine del percorso precedente DBLVECTOR vAxNew = vAxPrev ; if ( ! AddRise( vAxNew, ptPStart.z - ptPEnd.z, nPrevClPathId, bPrevMain)) return false ; } } // se corrente più basso else if ( nLowerPos == 1) { // inserisco la posizione a Z alta if ( ! AddRobotClimb( nEntId, ptPEnd.z - ptPStart.z)) return false ; } // altrimenti non devo fare alcunché } // altrimenti aggiungo risalita a Zmax else { // se precedente appartiene ad altra operazione if ( nPrevClPathId == GDB_ID_NULL) { // elimino eventuali risalite finali if ( pPrevOp == nullptr || ! pPrevOp->RemoveRise()) return false ; // aggiungo risalita a Z max alla fine della lavorazione precedente DBLVECTOR vAxNew = vAxPrev ; if ( ! pPrevOp->AddRise( vAxNew, NAN, GDB_ID_NULL, bPrevMain)) return false ; } // altrimenti precedente appartiene alla stessa operazione else { // eventuali risalite finali già eliminate dal chiamante // aggiungo risalita a Z max alla fine del percorso precedente DBLVECTOR vAxNew = vAxPrev ; if ( ! AddRise( vAxNew, NAN, nPrevClPathId, bPrevMain)) return false ; } // inserisco la posizione iniziale a Zmax in questo percorso if ( ! AddRobotClimb( nEntId)) return false ; } } } return true ; } //---------------------------------------------------------------------------- bool Operation::ToolChangeNeeded( const Operation& Ope1, bool bMain1, const Operation& Ope2, bool bMain2) const { // se confronto con gli utensili principali if ( bMain1 && bMain2) { // se non cambia l'utensile, non cambia la testa fisica if ( EqualNoCase( Ope1.GetToolName(), Ope2.GetToolName())) return false ; // se stanno sulla stessa testa e non è la stessa uscita, non cambia la testa fisica if ( EqualNoCase( Ope1.GetHeadName(), Ope2.GetHeadName()) && Ope1.GetExitNbr() != Ope2.GetExitNbr()) return false ; } // se confronto il principale di Ope1 con il secondario di Ope2 else if ( bMain1 && ! bMain2) { // verifico che la seconda operazione sia in doppio string sDblTool2, sDblTcPos2, sDblHead2 ; int nDblExitNbr2 ; if ( ! Ope2.GetDoubleToolData( sDblTool2, sDblTcPos2, sDblHead2, nDblExitNbr2)) return true ; // se non cambia l'utensile, non cambia la testa fisica if ( EqualNoCase( Ope1.GetToolName(), sDblTool2)) return false ; // se stanno sulla stessa testa e non è la stessa uscita, non cambia la testa fisica if ( EqualNoCase( Ope1.GetHeadName(), sDblHead2) && Ope1.GetExitNbr() != nDblExitNbr2) return false ; } // se confronto il secondario di Ope1 con il principale di Ope2 else if ( ! bMain1 && bMain2) { // verifico che la prima operazione sia in doppio string sDblTool1, sDblTcPos1, sDblHead1 ; int nDblExitNbr1 ; if ( ! Ope1.GetDoubleToolData( sDblTool1, sDblTcPos1, sDblHead1, nDblExitNbr1)) return true ; // se non cambia l'utensile, non cambia la testa fisica if ( EqualNoCase( sDblTool1, Ope2.GetToolName())) return false ; // se stanno sulla stessa testa e non è la stessa uscita, non cambia la testa fisica if ( EqualNoCase( sDblHead1, Ope2.GetHeadName()) && nDblExitNbr1 != Ope2.GetExitNbr()) return false ; } // se confronto utensili secondari else { // verifico che le due lavorazioni siano in doppio string sDblTool1, sDblTool2, sDblTcPos1, sDblTcPos2, sDblHead1, sDblHead2 ; int nDblExitNbr1, nDblExitNbr2 ; if ( ! Ope1.GetDoubleToolData( sDblTool1, sDblTcPos1, sDblHead1, nDblExitNbr1) || ! Ope2.GetDoubleToolData( sDblTool2, sDblTcPos2, sDblHead2, nDblExitNbr2)) return true ; // se non cambia l'utensile, non cambia la testa fisica if ( EqualNoCase( sDblTool1, sDblTool2)) return false ; // se stanno sulla stessa testa e non è la stessa uscita, non cambia la testa fisica if ( EqualNoCase( sDblHead1, sDblHead2) && nDblExitNbr1 != nDblExitNbr2) return false ; } // altrimenti necessario un cambio return true ; } //---------------------------------------------------------------------------- // Inserisce sempre prima della prima entità del percorso ( se bFirst falso dopo tutti i Climb già inseriti) bool Operation::AddSpecialClimb( const DBLVECTOR& vAxVal, bool bOk, int nClPathId, bool bMain, int nFlag, int nFlag2, int nMask, const string& sInfo, bool bFirst) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // se percorso CL specificato, verifico appartenga al gruppo della geometria di lavorazione (Cutter Location) if ( nClPathId != GDB_ID_NULL) { int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), ( bMain ? MCH_CL : MCH_DBL)) ; if ( nClId == GDB_ID_NULL || m_pGeomDB->GetParentId( nClPathId) != nClId) return false ; } // altrimenti, recupero il primo percorso CL non vuoto della operazione else { nClPathId = GetFirstFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return false ; } // cerco la prima entità del percorso int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; if ( ! bFirst) { string sName ; while ( nEntId != GDB_ID_NULL && m_pGeomDB->GetName( nEntId, sName) && EqualNoCase( sName, MCH_CL_CLIMB)) nEntId = m_pGeomDB->GetNext( nEntId) ; } if ( nEntId == GDB_ID_NULL) return false ; // ne recupero i dati Cam CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // verifico che il numero di valori sia corretto int nValCnt = ssize( vAxVal) ; int nAxesCnt = ssize( pCamData->GetAxesVal()) ; if ( nValCnt != nAxesCnt) { string sOut = "Error : " + ToString( nValCnt) + " values vs " + ToString( nAxesCnt) + " axes" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) return false ; } // verifico siano nelle corse bool bOsOk ; int nStat ; if ( ! m_pMchMgr->GetCurrIsRobot()) bOsOk = m_pMchMgr->VerifyOutstroke( vAxVal[0], vAxVal[1], vAxVal[2], DBLVECTOR{ vAxVal.begin() + 3, vAxVal.end()}, false, nStat) ; else bOsOk = m_pMchMgr->VerifyOutstroke( 0, 0, 0, vAxVal, false, nStat) ; if ( ! bOsOk || nStat != 0) { string sInfo = "Outstroke : " + m_pMchMgr->GetOutstrokeInfo() ; LOG_INFO( GetEMkLogger(), sInfo.c_str()) return false ; } // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( pCamData->GetEndPoint()) ; // inserisco l'oggetto nel DB geometrico prima della prima entità int nId = m_pGeomDB->InsertGeoObj( GDB_ID_NULL, nEntId, GDB_BEFORE, Release( pGP)) ; if ( nId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nId, MCH_CL_CLIMB) ; // creo oggetto dati Cam da associare al punto PtrOwner pCam( pCamData->Clone()) ; if ( IsNull( pCam)) return false ; // assegno i dati pCam->SetAxes( ( bOk ? CamData::AS_OK : CamData::AS_OUTSTROKE), vAxVal) ; if ( nMask >= 0) pCam->ChangeAxesMask( nMask) ; pCam->SetMoveType( 0) ; pCam->SetFeed( 0) ; pCam->SetFlag( nFlag) ; pCam->SetFlag2( nFlag2) ; // calcolo e assegno dati derivati DBLVECTOR vAng( next( vAxVal.begin(), 3), vAxVal.end()) ; Vector3d vtTool ; m_pMchMgr->GetCalcToolDirFromAngles( vAng, vtTool) ; pCam->SetToolDir( vtTool) ; if ( ! pCam->GetAuxDir().IsSmall()) { Vector3d vtAux ; m_pMchMgr->GetCalcAuxDirFromAngles( vAng, vtAux) ; pCam->SetAuxDir( vtAux) ; } // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nId, Release( pCam)) ; // inserzione eventuali info if ( ! IsEmptyOrSpaces( sInfo)) { STRVECTOR vsTokens ; Tokenize( sInfo, ";", vsTokens) ; for ( const auto& sToken : vsTokens) { if ( ! IsEmptyOrSpaces( sToken)) { string sKey, sVal ; SplitFirst( sToken, "=", sKey, sVal) ; if ( ! IsEmptyOrSpaces( sKey)) m_pGeomDB->SetInfo( nId, sKey, sVal) ; } } } return true ; } //---------------------------------------------------------------------------- bool Operation::RemoveClimb( int nClPathId, bool bMain) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // se percorso CL specificato, verifico appartenga al gruppo della geometria di lavorazione (Cutter Location) if ( nClPathId != GDB_ID_NULL) { int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), ( bMain ? MCH_CL : MCH_DBL)) ; if ( nClId == GDB_ID_NULL || m_pGeomDB->GetParentId( nClPathId) != nClId) return false ; } // altrimenti, recupero il primo percorso CL non vuoto della operazione else { nClPathId = GetFirstFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return false ; } // elimino tutte le entità CLIMB all'inizio del percorso int nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_CLIMB) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_CLIMB) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::AddRise( DBLVECTOR& vAxVal, double dDelta, int nClPathId, bool bMain, int nToMinMaxZ) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // se percorso CL specificato, verifico appartenga al gruppo della geometria di lavorazione (Cutter Location) if ( nClPathId != GDB_ID_NULL) { int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), ( bMain ? MCH_CL : MCH_DBL)) ; if ( nClId == GDB_ID_NULL || m_pGeomDB->GetParentId( nClPathId) != nClId) return false ; } // altrimenti, recupero l'ultimo percorso CL non vuoto della operazione else { nClPathId = GetLastFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return false ; } // recupero l'ultima entità del percorso int nEntId = m_pGeomDB->GetLastInGroup( nClPathId) ; if ( nEntId == GDB_ID_NULL) return false ; // ne recupero i dati Cam const CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // se risalita praticamente nulla, esco con successo if ( isfinite( dDelta) && abs( dDelta) < 10 * EPS_SMALL) { // recupero i valori degli assi vAxVal = pCamData->GetAxesVal() ; return ( vAxVal.size() >= 3) ; } // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( pCamData->GetEndPoint()) ; // inserisco l'oggetto nel DB geometrico int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nClPathId, Release( pGP)) ; if ( nId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nId, MCH_CL_RISE) ; // creo oggetto dati Cam da associare al punto PtrOwner pCam( pCamData->Clone()) ; if ( IsNull( pCam)) return false ; // recupero i valori degli assi vAxVal = pCam->GetAxesVal() ; // flag per tipo di movimento in rapido int nFlag = 0 ; int nFlag2 = 0 ; bool bOk = false ; // se centro di lavoro if ( m_pMchMgr->GetCurrIsMcent()) { // devono esserci almeno i tre assi lineari if ( vAxVal.size() < 3) return false ; // se delta definito lo uso come incremento di Z if ( isfinite( dDelta)) { vAxVal[2] += dDelta ; nFlag = 0 ; // movimento standard } // altrimenti uso HomeZ/MinZ/MaxZ else { // recupero posizione Z double dExtrZ ; if ( nToMinMaxZ == -1) m_pMchMgr->GetCurrAxisMin( 2, dExtrZ) ; else if ( nToMinMaxZ == +1) m_pMchMgr->GetCurrAxisMax( 2, dExtrZ) ; else m_pMchMgr->GetCurrAxisHomePos( 2, dExtrZ) ; vAxVal[2] = dExtrZ ; nFlag = 3 ; // movimento a Zmax nFlag2 = 1 ; } // eventuale aggiustamenti speciali dipendenti dalla macchina DBLVECTOR vAxTmp = vAxVal ; Vector3d vtTool = pCam->GetToolDir() ; int nFlagTmp = nFlag ; int nFlag2Tmp = nFlag2 ; bool bModif ; if ( ! SpecialMoveZup( vAxTmp, vtTool, nFlagTmp, nFlag2Tmp, bModif)) return false ; if ( bModif) { vAxVal = vAxTmp ; pCam->SetToolDir( vtTool) ; Vector3d vtAux = pCam->GetAuxDir() ; if ( ! vtAux.IsSmall()) { DBLVECTOR vAng( vAxVal.begin() + 3, vAxVal.end()) ; if ( m_pMchMgr->GetCalcAuxDirFromAngles( vAng, vtAux)) pCam->SetAuxDir( vtAux) ; } nFlag = nFlagTmp ; nFlag2 = nFlag2Tmp ; } // verifico extra-corsa asse Z double dL1 = vAxVal[0] ; double dL2 = vAxVal[1] ; double dL3 = vAxVal[2] ; DBLVECTOR vAng( vAxVal.begin() + 3, vAxVal.end()) ; int nStat ; bOk = ( m_pMchMgr->VerifyOutstroke( dL1, dL2, dL3, vAng, false, nStat) && nStat == 0) ; // assegno i dati pCam->SetAxes( ( bOk ? CamData::AS_OK : CamData::AS_OUTSTROKE), vAxVal) ; pCam->SetMoveType( 0) ; pCam->SetFeed( 0) ; pCam->SetFlag( nFlag) ; pCam->SetFlag2( nFlag2) ; // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nId, Release( pCam)) ; } // se altrimenti robot else if ( m_pMchMgr->GetCurrIsRobot()) { // devono esserci i sei assi rotanti if ( vAxVal.size() < 6) return false ; // verifico se richiesta Zmax bool bZmax = ( ! isfinite( dDelta)) ; // calcolo gli assi (compresa verifica corse) Point3d ptEnd = pCamData->GetEndPoint() ; Vector3d vtTool = pCamData->GetToolDir() ; Vector3d vtAux = pCamData->GetAuxDir() ; DBLVECTOR vAxRise = vAxVal ; double dNewDeltaZ ; bOk = CalcRobotAxesAbovePos( ptEnd, vtTool, vtAux, dDelta, vAxRise, &dNewDeltaZ) ; // assegno i dati pCam->SetAxes( ( bOk ? CamData::AS_OK : CamData::AS_OUTSTROKE), vAxRise) ; pCam->SetMoveType( 0) ; pCam->SetFeed( 0) ; pCam->SetFlag( ( bZmax ? 3 : 0)) ; pCam->SetFlag2( ( bZmax ? 1 : 0)) ; // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nId, Release( pCam)) ; // aggiungo eventuali posizioni intermedie per garantire movimento lineare entro tolleranze int nStep = 1 + int( ( dNewDeltaZ - EPS_SMALL) / MAX_ROBOT_G0_LEN) ; double dStep = dNewDeltaZ / nStep ; for ( int i = 1 ; i < nStep ; ++ i) { // copio l'entità int nNewId = m_pGeomDB->Copy( nId, GDB_ID_NULL, nId, GDB_BEFORE) ; if ( nNewId == GDB_ID_NULL) return false ; // calcolo la posizione sopra il punto a Zmax DBLVECTOR vAxNew = vAxVal ; if ( ! CalcRobotAxesAbovePos( ptEnd, vtTool, vtAux, i * dStep, vAxNew)) return false ; // modifico l'entità copiata CamData* pCdNew = GetCamData( m_pGeomDB->GetUserObj( nNewId)) ; if ( pCdNew == nullptr) return false ; pCdNew->SetAxes( CamData::AS_OK, vAxNew) ; pCdNew->SetFlag( 0) ; pCdNew->SetFlag2( -1) ; } } return bOk ; } //---------------------------------------------------------------------------- // Inserisce sempre dopo l'ultima entità del percorso bool Operation::AddSpecialRise( const DBLVECTOR& vAxVal, bool bOk, int nClPathId, bool bMain, int nFlag, int nFlag2, int nMask, const string& sInfo) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // se percorso CL specificato, verifico appartenga al gruppo della geometria di lavorazione (Cutter Location) if ( nClPathId != GDB_ID_NULL) { int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), ( bMain ? MCH_CL : MCH_DBL)) ; if ( nClId == GDB_ID_NULL || m_pGeomDB->GetParentId( nClPathId) != nClId) return false ; } // altrimenti, recupero l'ultimo percorso CL non vuoto della operazione else { nClPathId = GetLastFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return false ; } // recupero l'ultima entità del percorso int nEntId = m_pGeomDB->GetLastInGroup( nClPathId) ; if ( nEntId == GDB_ID_NULL) return false ; // ne recupero i dati Cam CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // verifico che il numero di valori sia corretto int nValCnt = ssize( vAxVal) ; int nAxesCnt = ssize( pCamData->GetAxesVal()) ; if ( nValCnt != nAxesCnt) { string sOut = "Error : " + ToString( nValCnt) + " values vs " + ToString( nAxesCnt) + " axes" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) return false ; } // verifico siano nelle corse bool bOsOk ; int nStat ; if ( ! m_pMchMgr->GetCurrIsRobot()) bOsOk = m_pMchMgr->VerifyOutstroke( vAxVal[0], vAxVal[1], vAxVal[2], DBLVECTOR{ vAxVal.begin() + 3, vAxVal.end()}, false, nStat) ; else bOsOk = m_pMchMgr->VerifyOutstroke( 0, 0, 0, vAxVal, false, nStat) ; if ( ! bOsOk || nStat != 0) { string sInfo = "Outstroke : " + m_pMchMgr->GetOutstrokeInfo() ; LOG_INFO( GetEMkLogger(), sInfo.c_str()) return false ; } // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( pCamData->GetEndPoint()) ; // inserisco l'oggetto nel DB geometrico int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nClPathId, Release( pGP)) ; if ( nId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nId, MCH_CL_RISE) ; // creo oggetto dati Cam da associare al punto PtrOwner pCam( pCamData->Clone()) ; if ( IsNull( pCam)) return false ; // assegno i dati pCam->SetAxes( ( bOk ? CamData::AS_OK : CamData::AS_OUTSTROKE), vAxVal) ; if ( nMask >= 0) pCam->ChangeAxesMask( nMask) ; pCam->SetMoveType( 0) ; pCam->SetFeed( 0) ; pCam->SetFlag( nFlag) ; pCam->SetFlag2( nFlag2) ; // calcolo e assegno dati derivati DBLVECTOR vAng( next( vAxVal.begin(), 3), vAxVal.end()) ; Vector3d vtTool ; m_pMchMgr->GetCalcToolDirFromAngles( vAng, vtTool) ; pCam->SetToolDir( vtTool) ; if ( ! pCam->GetAuxDir().IsSmall()) { Vector3d vtAux ; m_pMchMgr->GetCalcAuxDirFromAngles( vAng, vtAux) ; pCam->SetAuxDir( vtAux) ; } // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nId, Release( pCam)) ; // inserzione eventuali info if ( ! IsEmptyOrSpaces( sInfo)) { STRVECTOR vsTokens ; Tokenize( sInfo, ";", vsTokens) ; for ( const auto& sToken : vsTokens) { if ( ! IsEmptyOrSpaces( sToken)) { string sKey, sVal ; SplitFirst( sToken, "=", sKey, sVal) ; if ( ! IsEmptyOrSpaces( sKey)) m_pGeomDB->SetInfo( nId, sKey, sVal) ; } string sKey, sVal ; } } return true ; } //---------------------------------------------------------------------------- bool Operation::RemoveRise( int nClPathId, bool bMain) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // se percorso CL specificato, verifico appartenga al gruppo della geometria di lavorazione (Cutter Location) if ( nClPathId != GDB_ID_NULL) { int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), ( bMain ? MCH_CL : MCH_DBL)) ; if ( nClId == GDB_ID_NULL || m_pGeomDB->GetParentId( nClPathId) != nClId) return false ; } // altrimenti, recupero l'ultimo percorso CL non vuoto della operazione else { nClPathId = GetLastFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return false ; } // elimino tutte le entità RISE alla fine del percorso int nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_RISE) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_RISE) ; } // elimino a maggior ragione eventuali entità HOME nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::AddHome( bool bMain) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero l'ultimo percorso CL non vuoto int nLastPxClId = GetLastFullToolpath( bMain) ; if ( nLastPxClId == GDB_ID_NULL) return false ; // recupero l'ultima entità del percorso di nome RISE int nEntId = m_pGeomDB->GetLastNameInGroup( nLastPxClId, MCH_CL_RISE) ; if ( nEntId == GDB_ID_NULL) return false ; // ne recupero i dati Cam CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( pCamData->GetEndPoint()) ; // inserisco l'oggetto nel DB geometrico int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLastPxClId, Release( pGP)) ; if ( nId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nId, MCH_CL_HOME) ; // creo oggetto dati Cam PtrOwner pCam( pCamData->Clone()) ; if ( IsNull( pCam)) return false ; // flag per tipo di movimento in rapido (rapido a Home) int nFlag = 4 ; // recupero coordinate home DBLVECTOR vAxHome ; m_pMchMgr->GetAllCurrAxesHomePos( vAxHome) ; // assegno i dati pCam->SetAxes( CamData::AS_OK, vAxHome) ; pCam->SetMoveType( 0) ; pCam->SetFeed( 0) ; pCam->SetFlag( nFlag) ; // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nId, Release( pCam)) ; return true ; } //---------------------------------------------------------------------------- bool Operation::RemoveHome( bool bMain) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero l'ultimo percorso CL non vuoto int nClPathId = GetLastFullToolpath( bMain) ; if ( nClPathId == GDB_ID_NULL) return false ; // elimino tutte le entità HOME alla fine del percorso int nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::RemoveClimbRiseHome( bool bMain) { // elimino le entità CLIMB, RISE e HOME delle diverse path dell'operazione int nClId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, ( bMain ? MCH_CL : MCH_DBL)) ; int nClPathId = m_pGeomDB->GetFirstGroupInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { RemoveClimb( nClPathId, bMain) ; RemoveRise( nClPathId, bMain) ; nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ; } return ( nClId != GDB_ID_NULL) ; } //---------------------------------------------------------------------------- bool Operation::AddRobotClimb( int nEntId, double dDeltaZ) { // recupero i dati Cam dell'entità CamData* pCdEnt = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCdEnt == nullptr) return false ; DBLVECTOR vAxVal = pCdEnt->GetAxesVal() ; // verifico se richiesta Zmax bool bZmax = ( ! isfinite( dDeltaZ)) ; // calcolo la posizione sopra il punto a Z richiesta DBLVECTOR vAxClimb = vAxVal ; double dNewDeltaZ ; if ( ! CalcRobotAxesAbovePos( pCdEnt->GetEndPoint(), pCdEnt->GetToolDir(), pCdEnt->GetAuxDir(), dDeltaZ, vAxClimb, &dNewDeltaZ)) return false ; // aggiungo l'entità solo se necessario if ( dNewDeltaZ < 100 * EPS_SMALL) return true ; // copio l'entità int nNewEntId = m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_BEFORE) ; if ( nNewEntId == GDB_ID_NULL) return false ; // aggiorno il CamData pCdEnt = GetCamData( m_pGeomDB->GetUserObj( nNewEntId)) ; if ( pCdEnt == nullptr) return false ; // modifico quella originale (è la prima del percorso) m_pGeomDB->SetName( nNewEntId, MCH_CL_CLIMB) ; m_pGeomDB->RemoveInfo( nNewEntId, KEY_CL_DOUBLE) ; pCdEnt->SetAxes( CamData::AS_OK, vAxClimb) ; pCdEnt->SetFlag( 2) ; pCdEnt->SetFlag2( ( bZmax ? 1 : 0)) ; // aggiungo eventuali posizioni intermedie per garantire movimento lineare entro tolleranze int nStep = 1 + int( ( dNewDeltaZ - EPS_SMALL) / MAX_ROBOT_G0_LEN) ; double dStep = dNewDeltaZ / nStep ; for ( int i = 1 ; i < nStep ; ++ i) { // copio l'entità int nNewId = m_pGeomDB->Copy( nNewEntId, GDB_ID_NULL, nNewEntId, GDB_AFTER) ; if ( nNewId == GDB_ID_NULL) return false ; // calcolo la posizione sopra il punto a Zmax DBLVECTOR vAxNew = vAxVal ; if ( ! CalcRobotAxesAbovePos( pCdEnt->GetEndPoint(), pCdEnt->GetToolDir(), pCdEnt->GetAuxDir(), i * dStep, vAxNew)) return false ; // modifico l'entità copiata CamData* pCdNew = GetCamData( m_pGeomDB->GetUserObj( nNewId)) ; if ( pCdNew == nullptr) return false ; pCdNew->SetAxes( CamData::AS_OK, vAxNew) ; pCdNew->SetFlag( 0) ; pCdNew->SetFlag2( -1) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::CalcRobotAxesAbovePos( const Point3d& ptP, const Vector3d& vtT, const Vector3d& vtA, double dDeltaZ, DBLVECTOR& vAx, double* pdNewDeltaZ) const { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) { if ( pdNewDeltaZ != nullptr) *pdNewDeltaZ = 0 ; return false ; } // se DeltaZ non specificata vado a Zmax if ( ! isfinite( dDeltaZ)) { // recupero quota tavola Point3d ptRef1 ; pMch->GetCurrTableRef1( ptRef1) ; // recupero Zmax sopra tavola double dMaxOnTabZ ; if ( ! m_pGeomDB->GetInfo( pMch->GetCurrTable(), "MaxOnTabZ", dMaxOnTabZ)) dMaxOnTabZ = 600 ; // assegno la variazione di Z dDeltaZ = ptRef1.z + dMaxOnTabZ - ptP.z ; } if ( pdNewDeltaZ != nullptr) *pdNewDeltaZ = dDeltaZ ; // calcolo assi macchina Point3d ptNew( ptP.x, ptP.y, ptP.z + dDeltaZ) ; DBLVECTOR vAng1, vAng2 ; if ( ! m_pMchMgr->GetCurrMachine()->GetRobotAngles( ptNew, vtT, vtA, vAng1, vAng2)) return false ; // se non definita posizione precedente, scelgo la prima soluzione if ( vAx.size() < 6) { vAx = vAng1 ; } // altrimenti, scelgo la soluzione più vicina else { // porto gli angoli ai valori più vicini ai precedenti con offset di uno o più giri for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) m_pMchMgr->GetNearestAngleInStroke( i, vAx[i], vAng1[i]) ; for ( int i = 0 ; i < int( vAng2.size()) ; ++ i) m_pMchMgr->GetNearestAngleInStroke( i, vAx[i], vAng2[i]) ; // salvo precedente R6 double dPrevR6 = vAx[5] ; // minimizzo rotazione polso if ( vAng2.empty() || abs( vAng1[3] - vAx[3]) < abs( vAng2[3] - vAx[3]) + 10 * EPS_ANG_SMALL) vAx = vAng1 ; else vAx = vAng2 ; // controllo continuità di R6 dalla posizione precedente m_pMchMgr->GetNearestAngleInStroke( 5, dPrevR6, vAx[5]) ; } // verifico di essere nelle corse degli assi int nStat ; if ( ! m_pMchMgr->VerifyOutstroke( 0, 0, 0, vAx, false, nStat) || nStat != 0) return false ; return true ; } //---------------------------------------------------------------------------- bool Operation::CalcDeltaZForHeadRotation( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd, double& dDeltaZ) const { if ( m_pMchMgr == nullptr) return false ; // il numero di assi deve essere costante if ( vAxStart.size() != vAxEnd.size()) return false ; // se ci sono solo assi lineari, pongo delta Z nullo if ( vAxStart.size() <= 3) { dDeltaZ = 0 ; return true ; } // determino gli estremi del movimento rotatorio double dR1Start = ( vAxStart.size() >= 4 ? vAxStart[3] : 0) ; double dR2Start = ( vAxStart.size() >= 5 ? vAxStart[4] : 0) ; double dR3Start = ( vAxStart.size() >= 6 ? vAxStart[5] : 0) ; double dR1End = ( vAxEnd.size() >= 4 ? vAxEnd[3] : 0) ; double dR2End = ( vAxEnd.size() >= 5 ? vAxEnd[4] : 0) ; double dR3End = ( vAxEnd.size() >= 6 ? vAxEnd[5] : 0) ; // determino le variazioni di ciascun asse double dR1Delta = dR1End - dR1Start ; double dR2Delta = dR2End - dR2Start ; double dR3Delta = dR3End - dR3Start ; // divido il movimento in step di massimo 5 gradi int nStep = int( max( abs( dR1Delta), max( abs( dR2Delta), abs( dR3Delta))) / 5.0) + 1 ; double dR1Step = dR1Delta / nStep ; double dR2Step = dR2Delta / nStep ; double dR3Step = dR3Delta / nStep ; // calcolo double dL1 = vAxStart[0] ; double dL2 = vAxStart[1] ; double dL3 = vAxStart[2] ; double dZStart, dZmin ; DBLVECTOR vAng( 3) ; for ( int i = 0 ; i <= nStep ; ++ i) { vAng[0] = dR1Start + i * dR1Step ; vAng[1] = dR2Start + i * dR2Step ; vAng[2] = dR3Start + i * dR3Step ; Point3d ptTip ; if ( ! m_pMchMgr->GetCalcTipFromPositions( dL1, dL2, dL3, vAng, true, true, ptTip)) return false ; if ( i == 0) { dZStart = ptTip.z ; dZmin = dZStart ; } else if ( ptTip.z < dZmin) dZmin = ptTip.z ; } dDeltaZ = dZStart - dZmin ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetExtraZ( const DBLVECTOR& vAx1, const Vector3d& vtTool1, const DBLVECTOR& vAx2, const Vector3d& vtTool2, double dHomeZ, double& dExtraZ) const { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // Se nuova gestione collegamenti, non fa niente if ( m_pMchMgr->GetCurrMachine()->GetNewLinkMgr()) { dExtraZ = 0 ; return true ; } // Flag per posizione sicura in alto o in basso int nHeadId = pMch->GetCurrHead() ; bool bZHomeDown ; if ( ! m_pGeomDB->GetInfo( nHeadId, MCH_ZHOMEDOWN, bZHomeDown)) bZHomeDown = false ; // Estremi dell'asse Z (asse 3, quindi indice 2) double dAxZmax ; pMch->GetCurrAxisMax( 2, dAxZmax) ; double dAxZmin ; pMch->GetCurrAxisMin( 2, dAxZmin) ; // Se definito e correttamente eseguito script SpecialGetMaxZ double dMaxZ ; if ( SpecialGetMaxZ( vAx1, vtTool1, vAx2, vtTool2, dMaxZ)) { // se caso standard if ( ! bZHomeDown) { dExtraZ = min( dMaxZ, dAxZmax) - dHomeZ ; return ( dExtraZ > EPS_SMALL) ; } // altrimenti caso con posizione sicura in basso (teste da sotto) else { dExtraZ = max( dMaxZ, dAxZmin) - dHomeZ ; return ( dExtraZ < EPS_SMALL) ; } } // Altrimenti verifico se testa ha Info ZEXTRA DBLVECTOR vdVal ; if ( ! m_pGeomDB->GetInfo( nHeadId, MCH_ZEXTRA, vdVal) || vdVal.empty() || vdVal[0] < EPS_SMALL) return false ; // angolo verticale limite per applicare questo allontanamento extra double dMaxAngV = 30 ; if ( vdVal.size() >= 2) dMaxAngV = vdVal[1] ; // angolo verticale double dAngV = 0 ; if ( vAx1.size() >= 5 && vAx2.size() >= 5) dAngV = max( abs( vAx1[4]), abs( vAx2[4])) ; else if ( vAx2.size() >= 5) dAngV = abs( vAx2[4]) ; // verifica angolo verticale if ( dAngV < dMaxAngV) { // se caso standard if ( ! bZHomeDown) { double dZmax ; pMch->GetCurrAxisMax( 2, dZmax) ; dExtraZ = min( vdVal[0], dZmax - dHomeZ) ; return ( dExtraZ > EPS_SMALL) ; } // altrimenti caso con posizione sicura in basso (teste da sotto) else { double dZmin ; pMch->GetCurrAxisMin( 2, dZmin) ; dExtraZ = max( vdVal[0], dZmin - dHomeZ) ; return ( dExtraZ < EPS_SMALL) ; } } // Lascio provare a Zmax dExtraZ = 0 ; return true ; } //---------------------------------------------------------------------------- bool Operation::SpecialGetMaxZ( const DBLVECTOR& vAx1, const Vector3d& vtTool1, const DBLVECTOR& vAx2, const Vector3d& vtTool2, double& dMaxZ) const { // recupero la macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // verifico numero assi int nNumAx1 = int( vAx1.size()) ; int nNumAx2 = int( vAx2.size()) ; if ( nNumAx1 != nNumAx2 && nNumAx1 != 0 && ! vtTool1.IsSmall()) return false ; // costanti static const string EMC_VAR = "EMC" ; // tabella variabili locali per calcolo static const string EVAR_ERROR = ".ERR" ; // OUT (int) codice di errore ( 0 = ok) static const string EVAR_MAXZ = ".MAXZ" ; // OUT (double) valore Zmax static const string ON_SPECIAL_GETMAXZ = "OnSpecialGetMaxZ" ; // verifico definizione funzione if ( ! pMch->LuaExistsFunction( ON_SPECIAL_GETMAXZ)) return false ; // avvio bool bOk = true ; int nErr = 99 ; // imposto valori parametri bOk = bOk && pMch->LuaCreateGlobTable( EMC_VAR) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TOOL, GetToolName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_HEAD, GetHeadName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_EXIT, GetExitNbr()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TCPOS, GetToolTcPos()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_MCHID, GetOwner()) ; // valori degli assi bool bIsRobot = m_pMchMgr->GetCurrIsRobot() ; for ( int i = 1 ; i <= nNumAx1 ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisPrev( i, EMC_VAR, bIsRobot), vAx1[i-1]) ; for ( int i = 1 ; i <= nNumAx2 ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisValue( i, EMC_VAR, bIsRobot), vAx2[i-1]) ; // direzioni utensile bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TDIRP, vtTool1) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TDIR, vtTool2) ; // eseguo bOk = bOk && pMch->LuaCallFunction( ON_SPECIAL_GETMAXZ) ; // recupero valori parametri obbligatori bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_ERROR, nErr) && nErr == 0 ; if ( bOk && ! pMch->LuaGetGlobVar( EMC_VAR + EVAR_MAXZ, dMaxZ)) { bOk = false ; nErr = 91 ; } // reset bOk = pMch->LuaResetGlobVar( EMC_VAR) && bOk ; // segnalo errori if ( ! bOk) { string sOut = " Error in " + ON_SPECIAL_GETMAXZ + " (" + ToString( nErr) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } return bOk ; } //---------------------------------------------------------------------------- bool Operation::GetRotationAtZmax( void) const { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // Se nuova gestione collegamenti, non fa niente if ( m_pMchMgr->GetCurrMachine()->GetNewLinkMgr()) return false ; // Se testa con Info ROTATZMAX la recupero e restituisco abilitazione rotazione a Zmax int nHeadId = pMch->GetCurrHead() ; int nVal = 0 ; return ( m_pGeomDB->GetInfo( nHeadId, MCH_ROTATZMAX, nVal) && nVal != 0) ; } //---------------------------------------------------------------------------- bool Operation::ForcedZmax( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd, const BBox3d& b3Raws) const { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return true ; // Se nuova gestione collegamenti, non fa niente if ( m_pMchMgr->GetCurrMachine()->GetNewLinkMgr()) return false ; // Recupero numero assi e ne verifico la costanza int nLinAxes = m_pMchMgr->GetCurrLinAxes() ; int nRotAxes = m_pMchMgr->GetCurrRotAxes() ; if ( nLinAxes + nRotAxes != vAxStart.size() || nLinAxes + nRotAxes != vAxEnd.size()) return true ; // Verifico se richiesta risalita da note utente della lavorazione if ( GetUserNotesZmax() > 0) return true ; // Se testa con Info ZMAXONROT != 0 e movimento assi rotanti -> risalita a Zmax int nHeadId = pMch->GetCurrHead() ; DBLVECTOR vdVal ; if ( m_pGeomDB->GetInfo( nHeadId, MCH_ZMAXONROT, vdVal) && vdVal.size() >= 1 && lround(vdVal[0]) > 0) { // controllo altezza minima grezzi double dHmin = 100 * EPS_SMALL ; if ( vdVal.size() >= 3) dHmin = max( vdVal[2], dHmin) ; if ( b3Raws.GetDimZ() < dHmin) return false ; // controllo variazione angolare minima double dAngTol = 100 * EPS_ANG_SMALL ; if ( vdVal.size() >= 2) dAngTol = max( vdVal[1], dAngTol) ; for ( int i = nLinAxes ; i < nLinAxes + nRotAxes ; ++ i) { if ( abs( vAxEnd[i] - vAxStart[i]) > dAngTol) return true ; } } // Non forzata risalita a Zmax return false ; } //---------------------------------------------------------------------------- int Operation::GetUserNotesZmax( void) const { string sUserNotes ; if ( m_pMchMgr != nullptr && m_pMchMgr->GetMachiningParam( MPA_USERNOTES, sUserNotes)) { int nVal ; if ( GetValInNotes( sUserNotes, UN_STARTZMAX, nVal)) return nVal ; } return 0 ; } //---------------------------------------------------------------------------- double Operation::GetDeltaSafeZ( const Vector3d& vtTool) const { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // Leggo da testa Info ZSAFEDELTA DBLVECTOR vdVal ; if ( ! m_pGeomDB->GetInfo( pMch->GetCurrHead(), MCH_ZSAFEDELTA, vdVal) || vdVal.empty()) return 0 ; if ( vdVal.size() == 1) return vdVal[0] ; return ( vtTool.z > MIN_ZDIR_VERT_CHSAW ? vdVal[0] : vdVal[1]) ; } //---------------------------------------------------------------------------- bool Operation::GetZHomeDown( void) const { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // Leggo da testa Info ZHOMEDOWN (0=falso[default], altrimenti vero) bool bZHomeDown ; if ( ! m_pGeomDB->GetInfo( pMch->GetCurrHead(), MCH_ZHOMEDOWN, bZHomeDown)) bZHomeDown = false ; return bZHomeDown ; } //---------------------------------------------------------------------------- static bool ResetMachine( Machine* pMch) { // macchina non definita if ( pMch == nullptr) return false ; // Riporto la macchina in home pMch->ResetAllAxesPos( true, false) ; // Sgancio pezzi, grezzi e sottopezzi dalla tavola corrente pMch->UnlinkAllPartsFromGroups() ; pMch->UnlinkAllRawPartsFromGroups() ; pMch->UnlinkAllFixturesFromGroups() ; // tutto bene return true ; } //---------------------------------------------------------------------------- bool Operation::TestCollisionAvoid( const DBLVECTOR& vAxStart, double dStartOffsX, const DBLVECTOR& vAxEnd, int* pnLKAMO) const { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // Recupero tavola corrente string sTable ; if ( ! pMch->GetCurrTable( sTable)) return false ; // Recupero assi correnti e ne verifico la costanza STRVECTOR vAxName ; pMch->GetAllCurrAxesNames( vAxName) ; if ( vAxName.size() != vAxStart.size() || vAxName.size() != vAxEnd.size()) return false ; // Porto la macchina in home pMch->ResetAllAxesPos( true, false) ; // Elenco grezzi attivi INTVECTOR vRawId ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) vRawId.emplace_back( nRawId) ; nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // Aggancio pezzi attivi alla tavola corrente for ( const auto nRawId : vRawId) pMch->LinkRawPartToGroup( nRawId, sTable) ; // Elenco bloccaggi attivi INTVECTOR vFxtId ; int nFxtId = m_pMchMgr->GetFirstFixture() ; while ( nFxtId != GDB_ID_NULL) { vFxtId.emplace_back( nFxtId) ; nFxtId = m_pMchMgr->GetNextFixture( nFxtId) ; } // Aggancio bloccaggi alla tavola corrente for ( const auto nFxtId : vFxtId) pMch->LinkFixtureToGroup( nFxtId, sTable) ; // Aggiungo eventuale offset su X DBLVECTOR vMyAxStart = vAxStart ; if ( abs( dStartOffsX) > EPS_SMALL) vMyAxStart[0] += dStartOffsX ; // Lancio la verifica custom int nRes = SpecialTestCollisionAvoid( vMyAxStart, vAxEnd) ; if ( nRes != SCAV_TOTEST) { ResetMachine( pMch) ; return ( nRes == SCAV_AVOID) ; } // Verifica int nLinkAxisOrder = ( m_pMchMgr->GetCurrIsMcent() ? pMch->GetLinkAxesMoveOrder() : LKAMO_INTERP) ; // se ordine dipende da variazione angolare, verifico quale ordine effettivo if ( nLinkAxisOrder == LKAMO_HEAD_BEFORE_IF_SAME_ANG || nLinkAxisOrder == LKAMO_HEAD_AFTER_IF_SAME_ANG) { // cerco massima variazione degli assi rotanti double dRotDelta = 0 ; for ( int i = 3 ; i < int( vMyAxStart.size()) ; ++ i) dRotDelta = max( dRotDelta, abs( vAxEnd[i] - vMyAxStart[i])) ; // se non si supera il limite const double MAX_ROT_DELTA = 1 ; if ( dRotDelta < MAX_ROT_DELTA) { if ( nLinkAxisOrder == LKAMO_HEAD_BEFORE_IF_SAME_ANG) nLinkAxisOrder = LKAMO_HEAD_BEFORE ; else nLinkAxisOrder = LKAMO_HEAD_AFTER ; } else nLinkAxisOrder = LKAMO_INTERP ; } bool bCollAvoid ; // se ordine stabilito tra gli assi di tavola e testa (LKAMO_HEAD_BEFORE(-1)=prima testa, LKAMO_HEAD_AFTER(+1)=dopo testa) if ( nLinkAxisOrder == LKAMO_HEAD_BEFORE || nLinkAxisOrder == LKAMO_HEAD_AFTER) { // assegno il valore degli assi nella posizione intermedia opportuna DBLVECTOR vAxMid ; GetAxisMidForTestCollisionAvoid( vMyAxStart, vAxEnd, nLinkAxisOrder, pMch, vAxMid) ; // eseguo i test bCollAvoid = OneMoveTestCollisionAvoid( vAxName, vMyAxStart, vAxMid, pMch, vRawId, vFxtId) && OneMoveTestCollisionAvoid( vAxName, vAxMid, vAxEnd, pMch, vRawId, vFxtId) ; } // altrimenti interpolo tutti gli assi tra loro LKAMO_INTERP(0) else { bCollAvoid = OneMoveTestCollisionAvoid( vAxName, vMyAxStart, vAxEnd, pMch, vRawId, vFxtId) ; } // Se evitata collisione, assegno ordine di movimento usato if ( bCollAvoid && pnLKAMO != nullptr) { // assegno ordine movimenti necessari *pnLKAMO = nLinkAxisOrder ; // se non è movimento interpolato, verifico se anche questo la evita if ( nLinkAxisOrder != LKAMO_INTERP) { if ( OneMoveTestCollisionAvoid( vAxName, vMyAxStart, vAxEnd, pMch, vRawId, vFxtId)) *pnLKAMO = LKAMO_INTERP ; } } // Ripristino lo stato macchina ResetMachine( pMch) ; return bCollAvoid ; } //---------------------------------------------------------------------------- bool Operation::GetAxisMidForTestCollisionAvoid( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd, int nAxisOrder, Machine* pMch, DBLVECTOR& vAxMid) const { // reset del risultato vAxMid.clear() ; // verifico che i vettori degli assi abbiano la stessa dimensione e che la macchina sia definita if ( vAxStart.size() != vAxEnd.size() || pMch == nullptr) return false ; // assegno dimensione al risultato vAxMid.resize( vAxStart.size()) ; // ciclo sugli assi correnti, assegno opportunamente valori a seconda del tipo (testa o tavola) for ( int i = 0 ; i < int( vAxStart.size()) ; ++ i) { bool bLinear, bHead ; pMch->GetCurrAxisType( i, bLinear, bHead) ; if ( nAxisOrder == -1) vAxMid[i] = ( bHead ? vAxEnd[i] : vAxStart[i]) ; else if ( nAxisOrder == +1) { vAxMid[i] = ( bHead ? vAxStart[i] : vAxEnd[i]) ; } } // tutto bene return true ; } //---------------------------------------------------------------------------- bool Operation::OneMoveTestCollisionAvoid( const STRVECTOR& vAxName, const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd, Machine* pMch, const INTVECTOR& vRawId, const INTVECTOR& vFxtId) const { // Parti di tavola da considerare per la collisione INTVECTOR vTabCollId ; pMch->GetCurrTableCollGroups( vTabCollId) ; // Parti di testa e collegati da considerare per la collisione INTVECTOR vHeadCollId ; pMch->GetCurrHeadCollGroups( vHeadCollId) ; if ( vHeadCollId.empty()) return true ; // Recupero utensili veri e propri e li nascondo INTVECTOR vCutterId ; int nExitId = m_pGeomDB->GetFirstGroupInGroup( vHeadCollId[0]) ; while ( nExitId != GDB_ID_NULL) { int nExitStat ; if ( m_pGeomDB->GetStatus( nExitId, nExitStat) && nExitStat != GDB_ST_OFF) { int nToolId = m_pGeomDB->GetFirstGroupInGroup( nExitId) ; while ( nToolId != GDB_ID_NULL) { int nToolStat ; if ( m_pGeomDB->GetStatus( nToolId, nToolStat) && nToolStat != GDB_ST_OFF) { int nCutterId = m_pGeomDB->GetFirstNameInGroup( nToolId, TNA_TOOL_START + "*") ; while ( nCutterId != GDB_ID_NULL) { vCutterId.emplace_back( nCutterId) ; m_pGeomDB->SetStatus( nCutterId, GDB_ST_OFF) ; nCutterId = m_pGeomDB->GetNextName( nCutterId, TNA_TOOL_START + "*") ; } } nToolId = m_pGeomDB->GetNextGroup( nToolId) ; } } nExitId = m_pGeomDB->GetNextGroup( nExitId) ; } // distanza di sicurezza const double TOL_SAFEDIST = 5.0 ; double dSafeDist = GetSafeZ() ; dSafeDist = max( dSafeDist - TOL_SAFEDIST, MIN_SAFEDIST) ; // Vado nelle posizioni da controllare const int COLL_STEP_MIN = 1 ; const int COLL_STEP_STD = 16 ; double dMoveLen = 0 ; for ( int j = 0 ; j < int( vAxName.size()) ; ++ j) { bool bLinear, bHead ; pMch->GetCurrAxisType( j, bLinear, bHead) ; dMoveLen += abs( vAxEnd[j] - vAxStart[j]) * ( bLinear ? 1 : 10) ; } int nStep = ( dMoveLen < 10 ? COLL_STEP_MIN : COLL_STEP_STD) ; bool bCollide = false ; for ( int i = 0 ; i <= nStep && ! bCollide ; ++ i) { // Imposto la posizione double dCoeff = double( i) / nStep ; for ( int j = 0 ; j < int( vAxName.size()) ; ++ j) { double dPos = ( 1 - dCoeff) * vAxStart[j] + dCoeff * vAxEnd[j] ; pMch->SetAxisPos( vAxName[j], dPos) ; } // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { string sAxes = "Test Collision at " ; for ( int j = 0 ; j < int( vAxName.size()) ; ++ j) { double dPos ; pMch->GetAxisPos( vAxName[j], dPos) ; sAxes += vAxName[j] + "=" + ToString( dPos, 1) + " " ; } LOG_DBG_INFO( GetEMkLogger(), sAxes.c_str()) ; } // Determino sottobox e box della testa e degli altri oggetti da considerare per la collisione BOXIVECTOR vbiSH ; BBox3d b3Head ; for ( int nCId : vHeadCollId) { int nHId = m_pGeomDB->GetFirstInGroup( nCId) ; while ( nHId != GDB_ID_NULL) { BBox3d b3Tmp ; m_pGeomDB->GetGlobalBBox( nHId, b3Tmp, BBF_ONLY_VISIBLE) ; if ( ! b3Tmp.IsEmpty()) { b3Tmp.Expand( dSafeDist) ; b3Head.Add( b3Tmp) ; vbiSH.emplace_back( b3Tmp, nHId) ; } nHId = m_pGeomDB->GetNext( nHId) ; } // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { string sName ; if ( ! m_pGeomDB->GetName( nCId, sName)) sName = "---" ; string sOut = " " + sName + " : " + ToString( b3Head, 1) ; LOG_DBG_INFO( GetEMkLogger(), sOut.c_str()) ; } } // Aggiungo eventuali utensili veri e propri for ( int nCutterId : vCutterId) { BBox3d b3Tmp ; m_pGeomDB->GetGlobalBBox( nCutterId, b3Tmp, BBF_STANDARD) ; if ( ! b3Tmp.IsEmpty()) { b3Tmp.Expand( dSafeDist) ; b3Head.Add( b3Tmp) ; vbiSH.emplace_back( b3Tmp, nCutterId) ; } // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { string sName ; if ( ! m_pGeomDB->GetName( nCutterId, sName)) sName = "---" ; string sOut = " " + sName + " : " + ToString( b3Head, 1) ; LOG_DBG_INFO( GetEMkLogger(), sOut.c_str()) ; } } // Li confronto con i grezzi for ( const auto nRawId : vRawId) { // verifico i box BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw) ; // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { string sBox = " Raw : " + ( b3Raw.IsEmpty() ? string( "Empty Box") : ToString( b3Raw, 1)) ; LOG_DBG_INFO( GetEMkLogger(), sBox.c_str()) ; } // se i box interferiscono if ( ! b3Raw.IsEmpty() && b3Head.Overlaps( b3Raw)) { // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { LOG_DBG_INFO( GetEMkLogger(), " Can collide -->") ; } // solido del grezzo const ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nRawSolidId)) ; // riferimento del grezzo Frame3d frSolid ; m_pGeomDB->GetGlobFrame( nRawSolidId, frSolid) ; // verifico i sottobox for ( const auto& biSH : vbiSH) { if ( biSH.first.Overlaps( b3Raw)) { bool bCutter = ( find( vCutterId.begin(), vCutterId.end(), biSH.second) != vCutterId.end()) ; // box del solido parte della testa (se cutter è nascosto) BBox3d b3Hsol ; int nFlag = ( bCutter ? BBF_STANDARD : BBF_ONLY_VISIBLE) ; m_pGeomDB->GetLocalBBox( biSH.second, b3Hsol, nFlag) ; // riferimento della parte di testa Frame3d frHsol ; if ( m_pGeomDB->GetGdbType( biSH.second) == GDB_TY_GEO) m_pGeomDB->GetGlobFrame( biSH.second, frHsol) ; else m_pGeomDB->GetGroupGlobFrame( biSH.second, frHsol) ; // se cutter, ruoto riferimento di -90deg attorno a Xlocale (per compensare quanto fatto in LoadTool) quindi box di +90deg if ( bCutter) { b3Hsol.Rotate( ORIG, X_AX, 0, 1) ; frHsol.Rotate( frHsol.Orig(), frHsol.VersX(), 0, -1) ; } // dati geometrici del box Point3d ptMin ; Vector3d vtDiag ; b3Hsol.GetMinDim( ptMin, vtDiag.x, vtDiag.y, vtDiag.z) ; // lo porto nel riferimento del grezzo frHsol.ToLoc( frSolid) ; // se utensile cilindrico (faccia XY quadra e origine del riferimento nel suo centro) if ( abs( vtDiag.x - vtDiag.y) < 100 * EPS_SMALL && abs( vtDiag.x / 2 + ptMin.x) < 100 * EPS_SMALL && abs( vtDiag.y / 2 + ptMin.y) < 100 * EPS_SMALL) { // traslo il riferimento per avere il cilindro centrato sull'asse Y in positivo frHsol.Translate( frHsol.VersZ() * ptMin.z) ; // verifica di collisione tra cilindro e solido if ( pStm == nullptr || CDeCylClosedSurfTm( frHsol, max( vtDiag.x, vtDiag.y) / 2, vtDiag.z, *pStm, dSafeDist)) { bCollide = true ; break ; } } // altrimenti else { // traslo il riferimento per avere il box nel primo ottante frHsol.Translate( frHsol.VersX() * ptMin.x + frHsol.VersY() * ptMin.y + frHsol.VersZ() * ptMin.z) ; // verifica di collisione tra box e solido if ( pStm == nullptr || CDeBoxClosedSurfTm( frHsol, vtDiag, *pStm, dSafeDist)) { bCollide = true ; break ; } } } } if ( bCollide) break ; } } // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { LOG_DBG_INFO( GetEMkLogger(), ( bCollide ? " COLLIDE" : " avoid")) ; } // Se non trovata collisione, li confronto con oggetti da verificare della tavola if ( ! bCollide) { for ( const auto nTabCollId : vTabCollId) { // verifico i box BBox3d b3TabColl ; int nFlag = BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM ; m_pGeomDB->GetGlobalBBox( nTabCollId, b3TabColl, nFlag) ; // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { string sBox = " TabColl : " + ( b3TabColl.IsEmpty() ? string( "Empty Box") : ToString( b3TabColl, 1)) ; LOG_DBG_INFO( GetEMkLogger(), sBox.c_str()) ; } // se i box interferiscono if ( ! b3TabColl.IsEmpty() && b3Head.Overlaps( b3TabColl)) { // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { LOG_DBG_INFO( GetEMkLogger(), " Can collide -->") ; } // verifico i sottobox for ( const auto& biSH : vbiSH) { if ( biSH.first.Overlaps( b3TabColl)) { bCollide = true ; break ; } } if ( bCollide) break ; } } // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { LOG_DBG_INFO( GetEMkLogger(), ( bCollide ? " COLLIDE" : " avoid")) ; } } // Se non trovata collisione, li confronto con i bloccaggi if ( ! bCollide) { for ( const auto nFxtId : vFxtId) { // verifico i box BBox3d b3Fxt ; int nFlag = BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM ; m_pGeomDB->GetGlobalBBox( nFxtId, b3Fxt, nFlag) ; // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { string sBox = " Fxt : " + ( b3Fxt.IsEmpty() ? string( "Empty Box") : ToString( b3Fxt, 1)) ; LOG_DBG_INFO( GetEMkLogger(), sBox.c_str()) ; } // se i box interferiscono if ( ! b3Fxt.IsEmpty() && b3Head.Overlaps( b3Fxt)) { // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { LOG_DBG_INFO( GetEMkLogger(), " Can collide -->") ; } // verifico i sottobox for ( const auto& biSH : vbiSH) { if ( biSH.first.Overlaps( b3Fxt)) { bCollide = true ; break ; } } if ( bCollide) break ; } } // eventuale emissione Log if ( ExeGetDebugLevel() >= 5) { LOG_DBG_INFO( GetEMkLogger(), ( bCollide ? " COLLIDE" : " avoid")) ; } } } // Ripristino visualizzazione utensili veri e propri for ( int nCutterId : vCutterId) m_pGeomDB->SetStatus( nCutterId, GDB_ST_ON) ; return ( ! bCollide) ; } //---------------------------------------------------------------------------- int Operation::SpecialTestCollisionAvoid( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd) const { // recupero la macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // assi già verificati // costanti static const string EMC_VAR = "EMC" ; // tabella variabili locali per calcolo static const string EVAR_ERROR = ".ERR" ; // OUT (int) codice di errore (0=ok) static const string EVAR_RES = ".RES" ; // OUT (int) risultato (0=collide, 1=evita, 2=fare test standard) static const string ON_SPECIAL_TESTCOLLISIONAVOID = "OnSpecialTestCollisionAvoid" ; // verifico definizione funzione if ( ! pMch->LuaExistsFunction( ON_SPECIAL_TESTCOLLISIONAVOID)) return SCAV_TOTEST ; // avvio bool bOk = true ; int nErr = 99 ; int nRes = SCAV_TOTEST ; // imposto valori parametri bOk = bOk && pMch->LuaCreateGlobTable( EMC_VAR) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TOOL, GetToolName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_HEAD, GetHeadName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_EXIT, GetExitNbr()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TCPOS, GetToolTcPos()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_MCHID, GetOwner()) ; // valori degli assi bool bIsRobot = m_pMchMgr->GetCurrIsRobot() ; for ( int i = 1 ; i <= int( vAxStart.size()) ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisPrev( i, EMC_VAR, bIsRobot), vAxStart[i-1]) ; for ( int i = 1 ; i <= int( vAxEnd.size()) ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisValue( i, EMC_VAR, bIsRobot), vAxEnd[i-1]) ; // eseguo bOk = bOk && pMch->LuaCallFunction( ON_SPECIAL_TESTCOLLISIONAVOID) ; // recupero valori parametri obbligatori bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_ERROR, nErr) && nErr == 0 ; if ( bOk && ! pMch->LuaGetGlobVar( EMC_VAR + EVAR_RES, nRes)) { bOk = false ; nErr = 91 ; } // reset bOk = pMch->LuaResetGlobVar( EMC_VAR) && bOk ; // segnalo errori if ( ! bOk) { string sOut = " Error in " + ON_SPECIAL_TESTCOLLISIONAVOID + " (" + ToString( nErr) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } return ( bOk ? nRes : SCAV_ERROR) ; } //---------------------------------------------------------------------------- bool Operation::SpecialMoveZup( DBLVECTOR& vAx, Vector3d& vtTool, int& nFlag, int& nFlag2, bool& bModif) { // recupero la macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // costanti static const string EMC_VAR = "EMC" ; // tabella variabili locali per calcolo static const string EVAR_ERROR = ".ERR" ; // OUT (int) codice di errore ( 0 = ok) static const string EVAR_MODIF = ".MODIF" ; // OUT (bool) flag di modifica effettuata static const string ON_SPECIAL_MOVEZUP = "OnSpecialMoveZup" ; // se nuova gestione collegamenti o funzione non implementata if ( m_pMchMgr->GetCurrMachine()->GetNewLinkMgr() || ! pMch->LuaExistsFunction( ON_SPECIAL_MOVEZUP)) { bModif = false ; return true ; } // eseguo l'azione bool bOk = true ; int nErr = 99 ; // imposto valori parametri bOk = bOk && pMch->LuaCreateGlobTable( EMC_VAR) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_FLAG, nFlag) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_FLAG2, nFlag2) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TOOL, GetToolName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_HEAD, GetHeadName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_EXIT, GetExitNbr()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TCPOS, GetToolTcPos()) ; // direzione utensile bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TDIR, vtTool) ; // valore degli assi bool bIsRobot = m_pMchMgr->GetCurrIsRobot() ; int nNumAxes = int( vAx.size()) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisValue( i, EMC_VAR, bIsRobot), vAx[i-1]) ; // eseguo bOk = bOk && pMch->LuaCallFunction( ON_SPECIAL_MOVEZUP) ; // recupero valori parametri obbligatori bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_ERROR, nErr) && nErr == 0 ; if ( bOk) { if ( ! pMch->LuaGetGlobVar( EMC_VAR + EVAR_MODIF, bModif) || ! pMch->LuaGetGlobVar( EMC_VAR + GVAR_FLAG, nFlag) || ! pMch->LuaGetGlobVar( EMC_VAR + GVAR_FLAG2, nFlag2) || ! pMch->LuaGetGlobVar( EMC_VAR + GVAR_TDIR, vtTool)) nErr = 91 ; for ( int i = 1 ; i <= nNumAxes ; ++ i) { if ( ! pMch->LuaGetGlobVar( GetGlobVarAxisValue( i, EMC_VAR, bIsRobot), vAx[i-1])) nErr = 92 ; } bOk = ( nErr == 0) ; } // reset bOk = pMch->LuaResetGlobVar( EMC_VAR) && bOk ; // segnalo errori if ( ! bOk) { string sOut = " Error in " + ON_SPECIAL_MOVEZUP + " (" + ToString( nErr) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } return bOk ; } //---------------------------------------------------------------------------- bool Operation::SpecialMoveRapid( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd, DBLVECTOR& vAxNew, bool& bModif) { // recupero la macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // costanti static const string EMC_VAR = "EMC" ; // tabella variabili locali per calcolo static const string EVAR_ERROR = ".ERR" ; // OUT (int) codice di errore ( 0 = ok) static const string EVAR_MODIF = ".MODIF" ; // OUT (bool) flag di modifica effettuata static const string ON_SPECIAL_MOVERAPID = "OnSpecialMoveRapid" ; // se funzione non implementata if ( ! pMch->LuaExistsFunction( ON_SPECIAL_MOVERAPID)) { bModif = false ; return true ; } // eseguo l'azione bool bOk = true ; int nErr = 99 ; // imposto valori parametri bOk = bOk && pMch->LuaCreateGlobTable( EMC_VAR) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TOOL, GetToolName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_HEAD, GetHeadName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_EXIT, GetExitNbr()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TCPOS, GetToolTcPos()) ; // valore degli assi bool bIsRobot = m_pMchMgr->GetCurrIsRobot() ; int nNumAxes = int( vAxStart.size()) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisPrev( i, EMC_VAR, bIsRobot), vAxStart[i-1]) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisValue( i, EMC_VAR, bIsRobot), vAxEnd[i-1]) ; // eseguo bOk = bOk && pMch->LuaCallFunction( ON_SPECIAL_MOVERAPID, false) ; // recupero valori parametri obbligatori bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_ERROR, nErr) ; bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_MODIF, bModif) ; vAxNew.resize( nNumAxes) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaGetGlobVar( GetGlobVarAxisValue( i, EMC_VAR, bIsRobot), vAxNew[i-1]) ; // reset bOk = bOk && pMch->LuaResetGlobVar( EMC_VAR) ; // segnalo errori if ( nErr != 0) { bOk = false ; string sOut = " Error in " + ON_SPECIAL_MOVERAPID + " (" + ToString( nErr) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } return bOk ; } //---------------------------------------------------------------------------- bool Operation::SpecialLink( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd, int nLinkType, const Operation* pPrevOpe, bool bPrevMain, const Operation* pNextOpe, bool bNextMain, int nStartZMax, int nOtherLinkType) { // recupero la macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // costanti static const string EMC_VAR = "EMC" ; // tabella variabili locali per calcolo static const string EVAR_PREVMCHID = ".PREVMCHID" ; // IN (int) identificativo lavorazione precedente static const string EVAR_PREVPHASE = ".PREVPHASE" ; // IN (int) indice fase della lavorazione precedente static const string EVAR_PREVMAIN = ".PREVMAIN" ; // IN (bool) flag lavorazione principale o double precedente static const string EVAR_NEXTMCHID = ".NEXTMCHID" ; // IN (int) identificativo lavorazione successiva static const string EVAR_NEXTPHASE = ".NEXTPHASE" ; // IN (int) indice fase della lavorazione successiva static const string EVAR_NEXTMAIN = ".NEXTMAIN" ; // IN (bool) flag lavorazione principale o double successiva static const string EVAR_STARTZMAX = ".STARTZMAX" ; // IN (int) identificativo StartZMax static const string EVAR_LINKTYPE = ".LINKTYPE" ; // IN (int) codice tipo collegamento (1=Inizio, 2=Fine, 3=FinePrec+InizioSucc) static const string EVAR_OTHERLINKTYPE = ".OTHERLINKTYPE" ; // IN (int) codice tipo collegamento per altra lavorazione (0=Non Presente, 1=Inizio, 2=Fine, 3=FinePrec+InizioSucc) static const string EVAR_ERROR = ".ERR" ; // OUT (int) codice di errore ( 0 = ok) static const string ON_SPECIAL_LINK = "OnSpecialLink" ; // se la funzione non è definita, esco if ( ! pMch->LuaExistsFunction( ON_SPECIAL_LINK)) return true ; // eseguo bool bOk = true ; int nErr = 99 ; // imposto valori parametri bOk = bOk && pMch->LuaCreateGlobTable( EMC_VAR) ; string sToolName = GetToolName() ; string sHeadName = GetHeadName() ; int nExitNbr = GetExitNbr() ; string sTcPos = GetToolTcPos() ; if ( ( pPrevOpe != nullptr && ! bPrevMain) || ( pNextOpe != nullptr && ! bNextMain)) { if ( ! GetDoubleToolData( sToolName, sTcPos, sHeadName, nExitNbr)) return false ; } bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TOOL, sToolName) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_HEAD, sHeadName) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_EXIT, nExitNbr) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TCPOS, sTcPos) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_PREVMCHID, ( pPrevOpe != nullptr ? pPrevOpe->GetOwner() : GDB_ID_NULL)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_PREVMAIN, ( pPrevOpe != nullptr ? ( bPrevMain ? MCH_CL : MCH_DBL) : MCH_HOME)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_PREVPHASE, ( pPrevOpe != nullptr ? pPrevOpe->m_nPhase : 0)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_NEXTMCHID, ( pNextOpe != nullptr ? pNextOpe->GetOwner() : GDB_ID_NULL)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_NEXTPHASE, ( pNextOpe != nullptr ? pNextOpe->m_nPhase : 0)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_NEXTMAIN, ( pNextOpe != nullptr ? ( bNextMain ? MCH_CL : MCH_DBL) : MCH_HOME)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_STARTZMAX, nStartZMax) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_PHASE, m_nPhase) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_LINKTYPE, nLinkType) ; if ( nOtherLinkType == LINK_NULL) bOk = bOk && pMch->LuaResetGlobVar( EMC_VAR + EVAR_OTHERLINKTYPE) ; else bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_OTHERLINKTYPE, nOtherLinkType) ; // valore degli assi bool bIsRobot = m_pMchMgr->GetCurrIsRobot() ; int nNumAxes = ssize( vAxStart) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisPrev( i, EMC_VAR, bIsRobot), vAxStart[i-1]) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisValue( i, EMC_VAR, bIsRobot), vAxEnd[i-1]) ; // eseguo bOk = bOk && pMch->LuaCallFunction( ON_SPECIAL_LINK) ; // recupero valori parametri obbligatori bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_ERROR, nErr) && nErr == 0 ; // reset bOk = pMch->LuaResetGlobVar( EMC_VAR) && bOk ; // segnalo errori if ( ! bOk) { string sOut = " Error in " + ON_SPECIAL_LINK + " (" + ToString( nErr) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } return bOk ; } //---------------------------------------------------------------------------- bool Operation::SpecialPrevMachiningOffset( const Operation* pPrevOpe, double& dOffsetX) { // recupero la macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // costanti static const string EMC_VAR = "EMC" ; // tabella variabili locali per calcolo static const string EVAR_PREVMCHID = ".PREVMCHID" ; // IN (int) identificativo lavorazione precedente static const string EVAR_PREVPHASE = ".PREVPHASE" ; // IN (int) indice fase della lavorazione precedente static const string EVAR_CURRMCHID = ".CURRMCHID" ; // IN (int) identificativo lavorazione corrente static const string EVAR_CURRPHASE = ".CURRPHASE" ; // IN (int) indice fase della lavorazione corrente static const string EVAR_ERROR = ".ERR" ; // OUT (int) codice di errore ( 0 = ok) static const string EVAR_OFFSETX = ".PREVOFFSX" ; // OUT (double) valore dell'offset in X da applicare a lavorazione precedente static const string ON_SPECIAL_GPMO = "OnSpecialGetPrevMachiningOffset" ; // default dOffsetX = 0 ; // se la funzione non è definita, esco if ( ! pMch->LuaExistsFunction( ON_SPECIAL_GPMO)) return true ; // eseguo bool bOk = true ; int nErr = 99 ; // imposto valori parametri bOk = bOk && pMch->LuaCreateGlobTable( EMC_VAR) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_PREVMCHID, ( pPrevOpe != nullptr ? pPrevOpe->GetOwner() : GDB_ID_NULL)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_PREVPHASE, ( pPrevOpe != nullptr ? pPrevOpe->m_nPhase : 0)) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_CURRMCHID, GetOwner()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + EVAR_CURRPHASE, m_nPhase) ; // eseguo bOk = bOk && pMch->LuaCallFunction( ON_SPECIAL_GPMO) ; // recupero valori parametri obbligatori bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_ERROR, nErr) && nErr == 0 ; bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_OFFSETX, dOffsetX) ; // reset bOk = pMch->LuaResetGlobVar( EMC_VAR) && bOk ; // segnalo errori if ( ! bOk) { string sOut = " Error in " + ON_SPECIAL_GPMO + " (" + ToString( nErr) + ")" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } return bOk ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::GetAggrBottomData( const string& sHead, AggrBottom& agbData) const { // inizializzo la struttura agbData.Clear() ; // verifiche if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero identificativo della testa int nHeadId = m_pMchMgr->GetHeadId( sHead) ; if ( nHeadId == GDB_ID_NULL) return false ; // leggo i dati m_pGeomDB->GetInfo( nHeadId, MCH_AGB_TYPE, agbData.nType) ; m_pGeomDB->GetInfo( nHeadId, MCH_AGB_DMAX, agbData.dDMax) ; m_pGeomDB->GetInfo( nHeadId, MCH_AGB_ENCH, agbData.dEncH) ; m_pGeomDB->GetInfo( nHeadId, MCH_AGB_ENCV, agbData.dEncV) ; m_pGeomDB->GetInfo( nHeadId, MCH_AGB_MDIR, agbData.vtMDir) ; return true ; } //---------------------------------------------------------------------------- bool Operation::IsAggrBottom( const string& sHead) const { // verifiche if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero identificativo della testa int nHeadId = m_pMchMgr->GetHeadId( sHead) ; if ( nHeadId == GDB_ID_NULL) return false ; // recupero info se aggregato int nAggrBott = 0 ; return ( m_pGeomDB->GetInfo( nHeadId, MCH_AGB_TYPE, nAggrBott) && nAggrBott != 0) ; }