e8e757d4bd
- corretto Double di CamData con arco (non faceva il mirror della normale) - in CL aggiunto info su tipo di catena cinematica (KINTYPE=0/1/2).
6102 lines
258 KiB
C++
6102 lines
258 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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<ICAvToolSurfTm> 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<ICurveLine> 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<ICurveComposite> 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<ICurveComposite> 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<BBox1d> vMmAxAll( nTotAxes) ;
|
|
int nClPathId = m_pGeomDB->GetFirstInGroup( nClId) ;
|
|
while ( nClPathId != GDB_ID_NULL) {
|
|
vector<BBox1d> 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 i = 0 ; i < int( vAxRot.size()) ; ++ i)
|
|
vAng[i] = vAxRotPrec[i] * ( 1 - dCoeff) + vAxRot[i] * 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 ( size_t i = 0 ; i < vAxRot.size() ; ++ i)
|
|
vAngMid[i] = ( vAxRotPrec[i] + vAxRot[i]) / 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 ( size_t i = 0 ; i < vAxRot.size() ; ++ i)
|
|
vAngEnd[i] = vAxRot[i] ;
|
|
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<ICurve> 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 ( size_t i = 0 ; i < vAxRot.size() ; ++ i)
|
|
vAng[i] = vAxRotPrec[i] * ( 1 - dCoeff) + vAxRot[i] * 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) {
|
|
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 - 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 = vAng1[1] - vAxRotPrec[1] ;
|
|
double dR2Diff2 = vAng2[1] - vAxRotPrec[1] ;
|
|
if ( abs( dR2Diff1) > abs( dR2Diff2) && abs( 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<IGeoPoint3d> 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<CamData> 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<IGeoPoint3d> 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<CamData> 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)
|
|
{
|
|
// 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))
|
|
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)
|
|
{
|
|
// 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))
|
|
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))
|
|
return false ;
|
|
|
|
// ripristino la lavorazione corrente ed effettuo collegamento
|
|
m_pMchMgr->SetCurrMachining( GetOwner()) ;
|
|
if ( ! MoveHeadFromHomeToMach( bCurrMain, sCurrToolName, sCurrHeadName, nCurrExitNbr, nStartZMax))
|
|
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)
|
|
{
|
|
// 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))
|
|
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))
|
|
return false ;
|
|
// Dalla sua posizione di Home porto da Testa Double sul percorso Double corrente
|
|
if ( ! MoveHeadFromHomeToMach( false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, nStartZMax))
|
|
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))
|
|
return false ;
|
|
// Successivamente porto la testa Double dalla sua Home alla lavorazione corrente ( in doppio) sul percorso
|
|
if ( ! MoveHeadFromHomeToMach( false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, nStartZMax))
|
|
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))
|
|
return false ;
|
|
// Successivamente porto la testa Main dalla sua Home alla lavorazione corrente ( in doppio) sul percorso
|
|
if ( ! MoveHeadFromHomeToMach( true, GetToolName(), GetHeadName(), GetExitNbr(), nStartZMax))
|
|
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))
|
|
return false ;
|
|
// Dalla sua posizione di Home porto da Testa Double sul percorso Double corrente
|
|
if ( ! MoveHeadFromHomeToMach( false, sCurrDblTool, sCurrDblHead, nCurrDblExitNbr, nStartZMax))
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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) ;
|
|
// 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) ;
|
|
// 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) ;
|
|
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) ;
|
|
// 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) ;
|
|
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)
|
|
{
|
|
// 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) ;
|
|
|
|
// 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<IGeoPoint3d> 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<CamData> 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<IGeoPoint3d> 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<CamData> 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<IGeoPoint3d> 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<CamData> 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<IGeoPoint3d> 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<CamData> 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)
|
|
{
|
|
// 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_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, ( bPrevMain ? MCH_CL : MCH_DBL)) ;
|
|
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, ( bNextMain ? MCH_CL : MCH_DBL)) ;
|
|
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) ;
|
|
// 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) ;
|
|
}
|