EgtMachKernel (Preview) :
- primo Test Preview Utensile.
This commit is contained in:
+515
-75
@@ -19,13 +19,42 @@
|
||||
#include "OperUserNotesConst.h"
|
||||
#include "/EgtDev/Include/EGkGeoPoint3d.h"
|
||||
#include "/EgtDev/Include/EGkCurve.h"
|
||||
#include "/EgtDev/Include/EGkGeoVector3d.h"
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
|
||||
using namespace std ;
|
||||
|
||||
const string KEY_AXIS_GROUP = "PreviewAxisGroup" ;
|
||||
const string KEY_ROT_AXIS_VAL = "PreviewRotAxisVal" ; // salvata per ogni gruppo di Preview
|
||||
|
||||
// struttura per informazioni sugli assi
|
||||
struct PreviewAxisInfo {
|
||||
string sName ;
|
||||
double dVal ;
|
||||
bool bLinear ;
|
||||
PreviewAxisInfo( string sN, double dV, bool bL)
|
||||
: sName( sN), dVal( dV), bLinear( bL) {}
|
||||
} ;
|
||||
typedef vector<PreviewAxisInfo> VPREVIEWAXISINFO ;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
Machining::Machining( void)
|
||||
{
|
||||
m_nLookFlag = MCH_LOOK_TAB_HEAD ;
|
||||
m_nPreviewHeadId = GDB_ID_NULL ;
|
||||
m_nPreviewToolTip = GDB_ID_NULL ;
|
||||
m_vPreviewAxisIds = {} ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Machining::~Machining( void)
|
||||
{
|
||||
m_nLookFlag = MCH_LOOK_NONE ;
|
||||
m_nPreviewHeadId = GDB_ID_NULL ;
|
||||
m_nPreviewToolTip = GDB_ID_NULL ;
|
||||
m_vPreviewAxisIds.clear() ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -67,18 +96,18 @@ Machining:: NeedPrevHome( void) const
|
||||
bool
|
||||
Machining::GetStartPoint( Point3d& ptStart) const
|
||||
{
|
||||
// verifico validità gestore DB geometrico
|
||||
// verifico validità gestore DB geometrico
|
||||
if ( m_pGeomDB == nullptr)
|
||||
return false ;
|
||||
// recupero gruppo per geometria di lavorazione (Cutter Location)
|
||||
int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ;
|
||||
if ( nClId == GDB_ID_NULL)
|
||||
return false ;
|
||||
// recupero la prima entità del primo sottogruppo
|
||||
// recupero la prima entità del primo sottogruppo
|
||||
int nEntId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetFirstGroupInGroup( nClId)) ;
|
||||
if ( nEntId == GDB_ID_NULL)
|
||||
return false ;
|
||||
// recupero il punto iniziale di questa entità
|
||||
// recupero il punto iniziale di questa entità
|
||||
const IGeoObj* pGeoObj = m_pGeomDB->GetGeoObj( nEntId) ;
|
||||
if ( pGeoObj->GetType() == GEO_PNT3D) {
|
||||
ptStart = GetGeoPoint3d( pGeoObj)->GetPoint() ;
|
||||
@@ -94,18 +123,18 @@ Machining::GetStartPoint( Point3d& ptStart) const
|
||||
bool
|
||||
Machining::GetEndPoint( Point3d& ptEnd) const
|
||||
{
|
||||
// verifico validità gestore DB geometrico
|
||||
// verifico validità gestore DB geometrico
|
||||
if ( m_pGeomDB == nullptr)
|
||||
return false ;
|
||||
// recupero gruppo per geometria di lavorazione (Cutter Location)
|
||||
int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ;
|
||||
if ( nClId == GDB_ID_NULL)
|
||||
return false ;
|
||||
// recupero l'ultima entità dell'ultimo sottogruppo
|
||||
// recupero l'ultima entità dell'ultimo sottogruppo
|
||||
int nEntId = m_pGeomDB->GetLastInGroup( m_pGeomDB->GetLastGroupInGroup( nClId)) ;
|
||||
if ( nEntId == GDB_ID_NULL)
|
||||
return false ;
|
||||
// recupero il punto finale di questa entità
|
||||
// recupero il punto finale di questa entità
|
||||
const IGeoObj* pGeoObj = m_pGeomDB->GetGeoObj( nEntId) ;
|
||||
if ( pGeoObj->GetType() == GEO_PNT3D) {
|
||||
ptEnd = GetGeoPoint3d( pGeoObj)->GetPoint() ;
|
||||
@@ -119,14 +148,129 @@ Machining::GetEndPoint( Point3d& ptEnd) const
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
Machining::PrepareToolPreview( void) const
|
||||
Machining::UpdateToolView( int nLookFlag)
|
||||
{
|
||||
// verifico validità gestori DB geometrico e CAM
|
||||
// verifico validità del Flag
|
||||
if ( nLookFlag != MCH_LOOK_NONE && nLookFlag != MCH_LOOK_TAB_TOOL && nLookFlag != MCH_LOOK_TAB_HEAD)
|
||||
return false ;
|
||||
|
||||
// se stesso Flag, non faccio nulla
|
||||
if ( m_nLookFlag == nLookFlag)
|
||||
return true ;
|
||||
|
||||
// aggiorno il flag
|
||||
m_nLookFlag = nLookFlag ;
|
||||
|
||||
// se non esiste un gruppo di Preview, non faccio nulla
|
||||
int nStId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_ST) ;
|
||||
if ( nStId == GDB_ID_NULL)
|
||||
return true ;
|
||||
|
||||
// recupero la macchina corrente
|
||||
Machine* pMch = m_pMchMgr->GetCurrMachine() ;
|
||||
if ( pMch == nullptr)
|
||||
return false ;
|
||||
|
||||
// se non ho una testa corrente in Preview, non faccio nulla
|
||||
if ( m_nPreviewHeadId == GDB_ID_NULL) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
|
||||
// recupero l'identificativo del gruppo di base della macchina
|
||||
int nMBaseId = m_pGeomDB->GetFirstInGroup( pMch->GetMachineId()) ;
|
||||
|
||||
// se non devo visualizzare nulla, allora spengo il gruppo "ST"
|
||||
if ( m_nLookFlag == MCH_LOOK_NONE)
|
||||
m_pGeomDB->SetStatus( nStId, GDB_ST_OFF) ;
|
||||
// altrimenti
|
||||
else {
|
||||
m_pGeomDB->SetStatus( nStId, GDB_ST_ON) ;
|
||||
// contatori locali
|
||||
const int MAX_ITER = 50 ;
|
||||
bool bStop = false ;
|
||||
int nIter = 0 ;
|
||||
// se devo visualizzare solamente il gruppo della testa corrente
|
||||
if ( m_nLookFlag == MCH_LOOK_TAB_TOOL) {
|
||||
// la testa diventa visibile
|
||||
m_pGeomDB->SetStatus( m_nPreviewHeadId, GDB_ST_ON) ;
|
||||
// tutti i gruppi che contengono la testa diventano visibili fino a "ST", l'unico loro
|
||||
// figlio visibile deve essere quello che contiene la testa corrente
|
||||
int nCurrId = m_nPreviewHeadId ;
|
||||
while ( ! bStop && nIter < MAX_ITER) {
|
||||
int nParentId = m_pGeomDB->GetParentId( nCurrId) ;
|
||||
if ( nParentId == GDB_ID_NULL) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
m_pGeomDB->SetStatus( nParentId, GDB_ST_ON) ;
|
||||
int nChildId = m_pGeomDB->GetFirstGroupInGroup( nParentId) ;
|
||||
while ( nChildId != GDB_ID_NULL) {
|
||||
if ( m_pGeomDB->GetGdbType( nChildId) == GDB_TY_GROUP && nChildId == nCurrId)
|
||||
m_pGeomDB->SetStatus( nChildId, GDB_ST_ON) ;
|
||||
else
|
||||
m_pGeomDB->SetStatus( nChildId, GDB_ST_OFF) ;
|
||||
nChildId = m_pGeomDB->GetNextGroup( nChildId) ;
|
||||
}
|
||||
bStop = ( nParentId == nStId || nParentId == nMBaseId) ;
|
||||
nCurrId = nParentId ;
|
||||
++ nIter ;
|
||||
}
|
||||
}
|
||||
// se devo visualizzare tutta la gerarchia
|
||||
else {
|
||||
deque<int> dqHierarchy ;
|
||||
dqHierarchy.push_front( nStId) ;
|
||||
while ( ! bStop && nIter < MAX_ITER) {
|
||||
// recupero ed elimino dalla deque il primo Id memorizzato
|
||||
int nCurrId = dqHierarchy.front() ;
|
||||
dqHierarchy.pop_front() ;
|
||||
if ( nCurrId == GDB_ID_NULL) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
// rendo visibile tale Layer
|
||||
m_pGeomDB->SetStatus( nCurrId, GDB_ST_ON) ;
|
||||
// se il gruppo corrente è la testa, ho finito
|
||||
bStop = ( nCurrId == m_nPreviewHeadId) ;
|
||||
if ( ! bStop) {
|
||||
// tutti i gruppi figli diventano visibili
|
||||
int nChildId = m_pGeomDB->GetFirstInGroup( nCurrId) ;
|
||||
while ( nChildId != GDB_ID_NULL) {
|
||||
if ( m_pGeomDB->GetGdbType( nChildId) == GDB_TY_GROUP)
|
||||
dqHierarchy.push_back( nChildId) ;
|
||||
nChildId = m_pGeomDB->GetNext( nChildId) ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( auto Iter = dqHierarchy.begin() ; Iter != dqHierarchy.end() ; ++ Iter) {
|
||||
if ( m_pGeomDB->GetGdbType( *Iter) == GDB_TY_GROUP)
|
||||
m_pGeomDB->SetStatus( *Iter, GDB_ST_ON) ;
|
||||
}
|
||||
}
|
||||
++ nIter ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
Machining::PrepareToolPreview()
|
||||
{
|
||||
// verifico validità gestori DB geometrico e CAM
|
||||
if ( m_pGeomDB == nullptr || m_pMchMgr == nullptr)
|
||||
return false ;
|
||||
// creo o svuoto gruppo per anteprima utensile
|
||||
|
||||
// recupero la macchina corrente
|
||||
Machine* pMch = m_pMchMgr->GetCurrMachine() ;
|
||||
if ( pMch == nullptr)
|
||||
return false ;
|
||||
|
||||
// se non esite il Gruppo di Preview, lo aggiungo
|
||||
int nStId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_ST) ;
|
||||
// se non c'è, lo aggiungo
|
||||
if ( nStId == GDB_ID_NULL) {
|
||||
nStId = m_pGeomDB->AddGroup( GDB_ID_NULL, GetOwner(), Frame3d()) ;
|
||||
if ( nStId == GDB_ID_NULL)
|
||||
@@ -134,9 +278,10 @@ Machining::PrepareToolPreview( void) const
|
||||
m_pGeomDB->SetName( nStId, MCH_ST) ;
|
||||
m_pGeomDB->SetLevel( nStId, GDB_LV_TEMP) ;
|
||||
}
|
||||
// altrimenti lo svuoto
|
||||
// altrimenti lo svuoto
|
||||
else
|
||||
m_pGeomDB->EmptyGroup( nStId) ;
|
||||
|
||||
// se necessario, imposto l'utensile corrente
|
||||
string sCurrTool ; m_pMchMgr->GetCalcTool( sCurrTool) ;
|
||||
const_cast<Machining*>( this)->UpdateToolData() ;
|
||||
@@ -144,34 +289,269 @@ Machining::PrepareToolPreview( void) const
|
||||
if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr()))
|
||||
return false ;
|
||||
}
|
||||
// copio la testa della lavorazione nel gruppo
|
||||
int nHeadId = m_pMchMgr->GetHeadId( GetHeadName()) ;
|
||||
int nId = m_pGeomDB->CopyGlob( nHeadId, GDB_ID_NULL, nStId) ;
|
||||
m_pGeomDB->SetMode( nId, GDB_MD_STD) ;
|
||||
m_pGeomDB->SetStatus( nId, GDB_ST_OFF) ;
|
||||
// elimino eventuali gruppi opportunamente indicati
|
||||
int nSubId = m_pGeomDB->GetFirstGroupInGroup( nId) ;
|
||||
while ( nSubId != GDB_ID_NULL) {
|
||||
int nNextSubId = m_pGeomDB->GetNextGroup( nSubId) ;
|
||||
bool bShow = true ;
|
||||
if ( m_pGeomDB->GetInfo( nSubId, KEY_PREVIEWSHOW, bShow) && ! bShow)
|
||||
m_pGeomDB->Erase( nSubId) ;
|
||||
nSubId = nNextSubId ;
|
||||
|
||||
// recupero l'identificativo del gruppo di base della macchina
|
||||
int nMBaseId = m_pGeomDB->GetFirstInGroup( pMch->GetMachineId()) ;
|
||||
|
||||
// recupero l'identificativo della testa della lavorazione corrente e il set di teste associato
|
||||
string sHead = GetHeadName() ;
|
||||
int nHeadId = m_pMchMgr->GetHeadId( sHead) ;
|
||||
const STRVECTOR& vSetHead = pMch->GetHSet( sHead) ;
|
||||
INTVECTOR vSetHeadIds ; vSetHeadIds.reserve( ssize( vSetHead)) ;
|
||||
for ( const string& sSetHead : vSetHead)
|
||||
vSetHeadIds.push_back( pMch->GetHeadId( sSetHead)) ;
|
||||
|
||||
// recupero la tavola corrente
|
||||
int nCurrTab = pMch->GetCurrTable() ;
|
||||
|
||||
// il gruppo iniziale di costruzione della gerarchia è "ST"
|
||||
int nCurrHierarchyId = nStId ;
|
||||
|
||||
// Per prima cosa verifico se ci sono degli assi rotativi associati alla tavola, questi devono
|
||||
// essere inseriti in ordine inverso all'interno del gruppo "ST" e il loro versore associato
|
||||
// deve essere invertito
|
||||
// NB. Per il momento viene memorizzato solo l'inverso del vettore; le geometrie vengono scartate
|
||||
if ( nCurrTab != GDB_ID_NULL) {
|
||||
// scorro i gruppi rotativi che influenzano tale tavola
|
||||
bool bStop = false ;
|
||||
int nCurrGrp = nCurrTab ;
|
||||
const int MAX_ITER = 50 ;
|
||||
int nIter = 0 ;
|
||||
while ( ! bStop && nIter < MAX_ITER) {
|
||||
// recupero il Parent Id del gruppo corrente
|
||||
int nParentId = m_pGeomDB->GetParentId( nCurrGrp) ;
|
||||
bStop = ( nParentId == GDB_ID_NULL || nParentId == nMBaseId ||
|
||||
! pMch->IsRotaryAxisGroup( nParentId)) ;
|
||||
if ( ! bStop) {
|
||||
// recupero il suo Name, il suo Frame e creo il nuovo gruppo rotativo di Tavola
|
||||
string sName ;
|
||||
bool bOk = ( m_pGeomDB->GetName( nParentId, sName) && ! sName.empty()) ;
|
||||
Frame3d* pFrame = ( bOk ? m_pGeomDB->GetGroupFrame( nParentId) : nullptr) ;
|
||||
bOk = bOk && ( pFrame != nullptr) ;
|
||||
int nNewTabRotAxisId = ( bOk ? m_pGeomDB->AddGroup( GDB_ID_NULL, nCurrHierarchyId, pFrame) : GDB_ID_NULL) ;
|
||||
bOk = bOk && ( nNewTabRotAxisId != GDB_ID_NULL) &&
|
||||
m_pGeomDB->SetName( nNewTabRotAxisId, sName) ;
|
||||
double dAxisPos = 0. ;
|
||||
bOk = bOk && pMch->GetAxisPos( sName, dAxisPos) &&
|
||||
m_pGeomDB->SetInfo( nNewTabRotAxisId, KEY_ROT_AXIS_VAL, dAxisPos) ;
|
||||
if ( ! bOk) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
nCurrHierarchyId = nNewTabRotAxisId ;
|
||||
m_vPreviewAxisIds.push_back( nNewTabRotAxisId) ;
|
||||
// aggiungo il versore invertito
|
||||
int nChildId = m_pGeomDB->GetFirstInGroup( nParentId) ;
|
||||
while ( nChildId != GDB_ID_NULL) {
|
||||
const IGeoVector3d* pGV = GetGeoVector3d( m_pGeomDB->GetGeoObj( nChildId)) ;
|
||||
if ( pGV != nullptr) {
|
||||
PtrOwner<IGeoVector3d> pGVInv( CloneGeoVector3d( pGV)) ;
|
||||
if ( ! IsNull( pGVInv)) {
|
||||
pGVInv->Mirror( pGV->GetBase(), pGV->GetVector()) ;
|
||||
string sName ; m_pGeomDB->GetName( nChildId, sName) ;
|
||||
int nNewGVId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nNewTabRotAxisId, Release( pGVInv)) ;
|
||||
if ( nNewGVId == GDB_ID_NULL || ! m_pGeomDB->SetName( nNewGVId, sName) ||
|
||||
! m_pGeomDB->SetStatus( nNewGVId, GDB_ST_OFF)) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
break ;
|
||||
}
|
||||
}
|
||||
nChildId = m_pGeomDB->GetNext( nChildId) ;
|
||||
}
|
||||
nCurrGrp = nParentId ;
|
||||
}
|
||||
++ nIter ;
|
||||
}
|
||||
}
|
||||
return ( nId != GDB_ID_NULL) ;
|
||||
|
||||
// A partire dal gruppo corrente della gerarchia devo inserire tutti gli assi angolari
|
||||
// che contengono la testa attuale.
|
||||
// - Gli assi Lineari vengono scartati.
|
||||
// - Gli assi Angolari sono rappresentati solo ed esclusivamente dal loro vettore.
|
||||
// - I Gruppi Figli degli assi Angolari che non sono altri assi Angolari vengono memorizzati
|
||||
// solamente se presentano il flag "PreviewShow" a 1.
|
||||
// - I Gruppi figli degli assi Angolari che non sono presenti nell'HeadSet corrente vengono visualizzati
|
||||
// - Se percorrendo la Catena cinamentica dalla Head al Gruppo Base incontro un asse Lineare, allora
|
||||
// tutti gli assi Angolari successivi non presenteranno Geometrie Attive, solamente il vettore.
|
||||
struct HierarchyAxis {
|
||||
int nId ;
|
||||
string sName ;
|
||||
bool bRotary ;
|
||||
HierarchyAxis( int nI, const string& sN, bool bR)
|
||||
: nId( nI), sName( sN), bRotary( bR) {} ;
|
||||
} ;
|
||||
vector<HierarchyAxis> vHierarchyAxis ;
|
||||
set<int> setAxisIdNoPreview ;
|
||||
// parto dal gruppo HeadId e a ritroso recupero la gerarchia
|
||||
int nCurrGrp = nHeadId ;
|
||||
const int MAX_ITER = 50 ;
|
||||
int nIter = 0 ;
|
||||
bool bFoundLinear = false ;
|
||||
while ( nIter < MAX_ITER) {
|
||||
// recupero il Parent Id del gruppo corrente
|
||||
int nParentId = m_pGeomDB->GetParentId( nCurrGrp) ;
|
||||
if ( nParentId == GDB_ID_NULL || nParentId == nMBaseId)
|
||||
break ;
|
||||
// verifico se asse Lineare o Rotativo
|
||||
bool bRotary = pMch->IsRotaryAxisGroup( nParentId) ;
|
||||
if ( ! bFoundLinear && ! bRotary)
|
||||
bFoundLinear = true ;
|
||||
// recupero il nome dell'Asse
|
||||
string sAxName ; m_pGeomDB->GetName( nParentId, sAxName) ;
|
||||
// memorizzo l'asse
|
||||
vHierarchyAxis.emplace_back( nParentId, sAxName, bRotary) ;
|
||||
// aggiorno i controlli
|
||||
if ( bFoundLinear)
|
||||
setAxisIdNoPreview.insert( nParentId) ;
|
||||
nCurrGrp = nParentId ;
|
||||
++ nIter ;
|
||||
}
|
||||
|
||||
// se non c'è alcuna gerarchia, allora inserisco solo la testa corrente
|
||||
if ( vHierarchyAxis.empty()) {
|
||||
m_nPreviewHeadId = m_pGeomDB->CopyGlob( nHeadId, GDB_ID_NULL, nCurrHierarchyId) ;
|
||||
if ( m_nPreviewHeadId == GDB_ID_NULL) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
// se esiste una gerarchia, allora la ricreo l'albero
|
||||
else {
|
||||
for ( auto Iter = vHierarchyAxis.rbegin() ; Iter != vHierarchyAxis.rend() ; ++ Iter) {
|
||||
int nParentId = Iter->nId ;
|
||||
// recupero il suo Name, il Frame e creo il nuovo gruppo
|
||||
string sParentName ;
|
||||
bool bOk = ( m_pGeomDB->GetName( nParentId, sParentName) && ! sParentName.empty()) ;
|
||||
Frame3d* pParentFrame = ( bOk ? m_pGeomDB->GetGroupFrame( nParentId) : nullptr) ;
|
||||
bOk = bOk && ( pParentFrame != nullptr) ;
|
||||
int nNewParentId = ( bOk ? m_pGeomDB->AddGroup( GDB_ID_NULL, nCurrHierarchyId, *pParentFrame) : GDB_ID_NULL) ;
|
||||
bOk = bOk && ( nNewParentId != GDB_ID_NULL) &&
|
||||
m_pGeomDB->SetName( nNewParentId, sParentName) ;
|
||||
if ( bOk) {
|
||||
// recupero il tipo di asse
|
||||
bool bRotary = ( pMch->IsRotaryAxisGroup( nParentId)) ;
|
||||
if ( bRotary) {
|
||||
m_vPreviewAxisIds.push_back( nNewParentId) ;
|
||||
// memorizzo la sua posizione iniziale
|
||||
double dAxisPos = 0. ;
|
||||
bOk = bOk && pMch->GetAxisPos( sParentName, dAxisPos) &&
|
||||
m_pGeomDB->SetInfo( nNewParentId, KEY_ROT_AXIS_VAL, dAxisPos) ;
|
||||
}
|
||||
// scorro i suoi figli
|
||||
int nChildId = m_pGeomDB->GetFirstInGroup( nParentId) ;
|
||||
while ( bOk && nChildId != GDB_ID_NULL) {
|
||||
// se vettore ausiliario lo inserisco solo se asse padre rotativo
|
||||
if ( m_pGeomDB->GetGdbType( nChildId) == GDB_TY_GEO) {
|
||||
if ( bRotary) {
|
||||
const IGeoVector3d* pGV = GetGeoVector3d( m_pGeomDB->GetGeoObj( nChildId)) ;
|
||||
if ( pGV != nullptr) {
|
||||
int nNewChildId = m_pGeomDB->CopyGlob( nChildId, GDB_ID_NULL, nNewParentId) ;
|
||||
bOk = ( nNewChildId != GDB_ID_NULL && m_pGeomDB->SetStatus( nNewChildId, GDB_ST_OFF)) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
// se gruppo
|
||||
else if ( m_pGeomDB->GetGdbType( nChildId) == GDB_TY_GROUP) {
|
||||
// se gruppo testa lo inserisco a prescindere
|
||||
if ( nChildId == nHeadId) {
|
||||
m_nPreviewHeadId = m_pGeomDB->CopyGlob( nHeadId, GDB_ID_NULL, nNewParentId) ;
|
||||
bOk = ( m_nPreviewHeadId == GDB_ID_NULL) ;
|
||||
}
|
||||
else {
|
||||
// se asse successivo nella catena cinematica, sarà il nuovo gruppo padre
|
||||
auto NextIter = Iter ; ++ NextIter ;
|
||||
if ( NextIter != vHierarchyAxis.rend() && NextIter->nId == nChildId)
|
||||
;
|
||||
// altrimenti
|
||||
else {
|
||||
// se asse precedente ad un asse lineare, non lo inserisco
|
||||
auto Iter = setAxisIdNoPreview.find( nParentId) ;
|
||||
if ( Iter != setAxisIdNoPreview.end())
|
||||
;
|
||||
// altrimenti controllo il valore del Flag di visualizzazione
|
||||
else {
|
||||
bool bPreviewShow = false ;
|
||||
if ( m_pGeomDB->GetInfo( nChildId, KEY_PREVIEWSHOW, bPreviewShow) && bPreviewShow) {
|
||||
int nNewChildId = m_pGeomDB->CopyGlob( nChildId, GDB_ID_NULL, nNewParentId) ;
|
||||
bOk = ( nNewChildId != GDB_ID_NULL) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nChildId = m_pGeomDB->GetNext( nChildId) ;
|
||||
}
|
||||
nCurrHierarchyId = nNewParentId ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recupero il gruppo dove inserire il punto di tip dell'utensile
|
||||
int nExitGrpId = pMch->GetCurrExit() ;
|
||||
if ( nExitGrpId == GDB_ID_NULL)
|
||||
return false ;
|
||||
// recupero il name di tale gruppo, devo confrontarlo con la copia di tale gruppo sotto "ST"
|
||||
string sExitGrpName ; m_pGeomDB->GetName( nExitGrpId, sExitGrpName) ;
|
||||
// dentro la gerarchia della testa sotto al gruppo "ST" ricerco il gruppo dell'uscita
|
||||
deque<int> dqIds ; dqIds.push_front( m_nPreviewHeadId) ;
|
||||
const int MAX_TRY = 50 ;
|
||||
int nCount = 0 ;
|
||||
int nNewExitGrpId = GDB_ID_NULL ;
|
||||
while ( ! dqIds.empty() && nCount < MAX_TRY) {
|
||||
int nId = dqIds.front() ;
|
||||
dqIds.pop_front() ;
|
||||
string sName ; m_pGeomDB->GetName( nId, sName) ;
|
||||
if ( EqualNoCase( sName, sExitGrpName)) {
|
||||
nNewExitGrpId = nId ;
|
||||
break ;
|
||||
}
|
||||
int nChildId = m_pGeomDB->GetFirstGroupInGroup( nId) ;
|
||||
while ( nChildId != GDB_ID_NULL) {
|
||||
dqIds.push_back( nChildId) ;
|
||||
nChildId = m_pGeomDB->GetNextGroup( nChildId) ;
|
||||
}
|
||||
++ nCount ;
|
||||
}
|
||||
if ( nNewExitGrpId == GDB_ID_NULL) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
// Inserisco il punto di Tip ( non visibile)
|
||||
PtrOwner<IGeoPoint3d> ptGToolTip( CreateGeoPoint3d()) ;
|
||||
if ( IsNull( ptGToolTip) || ! ptGToolTip->Set( ORIG - Z_AX * GetToolData().m_dLen))
|
||||
return false ;
|
||||
m_nPreviewToolTip = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nNewExitGrpId, Release( ptGToolTip)) ;
|
||||
if ( m_nPreviewToolTip == GDB_ID_NULL ||
|
||||
! m_pGeomDB->SetStatus( m_nPreviewToolTip, GDB_ST_ON))
|
||||
return false ;
|
||||
|
||||
// aggiorno la Preview a seconda della modalità
|
||||
if ( ! UpdateToolView( MCH_LOOK_TAB_TOOL)) {
|
||||
RemoveToolPreview() ;
|
||||
return false ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
Machining::RemoveToolPreview( void) const
|
||||
Machining::RemoveToolPreview( void)
|
||||
{
|
||||
// verifico validità gestore DB geometrico
|
||||
// verifico validità gestore DB geometrico
|
||||
if ( m_pGeomDB == nullptr)
|
||||
return false ;
|
||||
// recupero gruppo per anteprima utensile
|
||||
int nStId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_ST) ;
|
||||
// lo svuoto
|
||||
m_pGeomDB->EmptyGroup( nStId) ;
|
||||
// reset delle variabili membro
|
||||
m_nPreviewHeadId = GDB_ID_NULL ;
|
||||
m_nPreviewToolTip = GDB_ID_NULL ;
|
||||
m_nLookFlag = MCH_LOOK_NONE ;
|
||||
m_vPreviewAxisIds.clear() ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
@@ -179,31 +559,37 @@ Machining::RemoveToolPreview( void) const
|
||||
int
|
||||
Machining::GetToolPreviewStepCount( void) const
|
||||
{
|
||||
// verifico validità gestori DB geometrico e CAM
|
||||
// verifico validità gestori DB geometrico e CAM
|
||||
if ( m_pGeomDB == nullptr || m_pMchMgr == nullptr)
|
||||
return 0 ;
|
||||
// recupero gruppo per geometria di lavorazione (Cutter Location)
|
||||
int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ;
|
||||
if ( nClId == GDB_ID_NULL)
|
||||
return 0 ;
|
||||
// determino il numero di entità di tutti i sottogruppi, escludendo CLIMB e RISE
|
||||
// determino il numero di entità di tutti i sottogruppi, escludendo CLIMB e RISE
|
||||
int nCount = 0 ;
|
||||
int nPxId = m_pGeomDB->GetFirstGroupInGroup( nClId) ;
|
||||
while ( nPxId != GDB_ID_NULL) {
|
||||
// aggiungo tutte le entità del truppo
|
||||
// aggiungo tutte le entità del truppo
|
||||
nCount += m_pGeomDB->GetGroupObjs( nPxId) ;
|
||||
// tolgo le entità CLIMB
|
||||
// tolgo le entità CLIMB
|
||||
int nClimbId = m_pGeomDB->GetFirstNameInGroup( nPxId, MCH_CL_CLIMB) ;
|
||||
while ( nClimbId != GDB_ID_NULL) {
|
||||
-- nCount ;
|
||||
nClimbId = m_pGeomDB->GetNextName( nClimbId, MCH_CL_CLIMB) ;
|
||||
}
|
||||
// tolgo le entità RISE
|
||||
// tolgo le entità RISE
|
||||
int nRiseId = m_pGeomDB->GetFirstNameInGroup( nPxId, MCH_CL_RISE) ;
|
||||
while ( nRiseId != GDB_ID_NULL) {
|
||||
-- nCount ;
|
||||
nRiseId = m_pGeomDB->GetNextName( nRiseId, MCH_CL_RISE) ;
|
||||
}
|
||||
// tolgo le entità di tipo HOME
|
||||
int nHomeId = m_pGeomDB->GetFirstNameInGroup( nPxId, MCH_CL_HOME) ;
|
||||
while ( nHomeId != GDB_ID_NULL) {
|
||||
-- nCount ;
|
||||
nHomeId = m_pGeomDB->GetNextName( nHomeId, MCH_CL_HOME) ;
|
||||
}
|
||||
// passo al successivo sottogruppo
|
||||
nPxId = m_pGeomDB->GetNextGroup( nPxId) ;
|
||||
}
|
||||
@@ -223,13 +609,13 @@ Machining::GetToolPreviewNext( int nEntId, int nParentId, int nStId) const
|
||||
// ciclo nel gruppo
|
||||
while ( nNewId != GDB_ID_NULL) {
|
||||
string sName ; m_pGeomDB->GetName( nNewId, sName) ;
|
||||
if ( sName != MCH_CL_CLIMB && sName != MCH_CL_RISE)
|
||||
if ( sName != MCH_CL_CLIMB && sName != MCH_CL_RISE && sName != MCH_CL_HOME)
|
||||
break ;
|
||||
nNewId = m_pGeomDB->GetNext( nNewId) ;
|
||||
}
|
||||
// se trovata, esco
|
||||
if ( nNewId != GDB_ID_NULL) {
|
||||
// se prima entità, eventuale attivazione uscite di gruppo a forare
|
||||
// se prima entità, eventuale attivazione uscite di gruppo a forare
|
||||
if ( nEntId == GDB_ID_NULL && m_pGeomDB->ExistsInfo( nNewParentId, KEY_DRACEX)) {
|
||||
INTVECTOR vActExit ;
|
||||
if ( m_pGeomDB->GetInfo( nNewParentId, KEY_DRACEX, vActExit))
|
||||
@@ -245,7 +631,7 @@ Machining::GetToolPreviewNext( int nEntId, int nParentId, int nStId) const
|
||||
if ( m_pGeomDB->GetInfo( nNewParentId, KEY_DRACEX, vActExit))
|
||||
ActivateDrillingUnit( nStId, vActExit) ;
|
||||
}
|
||||
// recupero la prima entità del successivo gruppo
|
||||
// recupero la prima entità del successivo gruppo
|
||||
nNewId = m_pGeomDB->GetFirstInGroup( nNewParentId) ;
|
||||
} while ( nNewId != GDB_ID_NULL) ;
|
||||
return GDB_ID_NULL ;
|
||||
@@ -253,8 +639,16 @@ Machining::GetToolPreviewNext( int nEntId, int nParentId, int nStId) const
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int
|
||||
Machining::GetToolPreviewPrev( int nEntId, int nParentId, int nStId) const
|
||||
Machining::GetToolPreviewPrev( int nEntId, int nParentId, int nStId) /*const*/
|
||||
{
|
||||
|
||||
if ( m_nLookFlag == MCH_LOOK_TAB_HEAD)
|
||||
UpdateToolView( MCH_LOOK_TAB_TOOL) ;
|
||||
else if ( m_nLookFlag == MCH_LOOK_TAB_TOOL)
|
||||
UpdateToolView( MCH_LOOK_NONE) ;
|
||||
else
|
||||
UpdateToolView( MCH_LOOK_TAB_HEAD) ;
|
||||
|
||||
// recupero la precedente
|
||||
int nNewId = (( nEntId != GDB_ID_NULL) ? m_pGeomDB->GetPrev( nEntId) : m_pGeomDB->GetLastInGroup( nParentId)) ;
|
||||
int nNewParentId = nParentId ;
|
||||
@@ -263,7 +657,7 @@ Machining::GetToolPreviewPrev( int nEntId, int nParentId, int nStId) const
|
||||
// ciclo nel gruppo
|
||||
while ( nNewId != GDB_ID_NULL) {
|
||||
string sName ; m_pGeomDB->GetName( nNewId, sName) ;
|
||||
if ( sName != MCH_CL_CLIMB && sName != MCH_CL_RISE)
|
||||
if ( sName != MCH_CL_CLIMB && sName != MCH_CL_RISE && sName != MCH_CL_HOME)
|
||||
break ;
|
||||
nNewId = m_pGeomDB->GetPrev( nNewId) ;
|
||||
}
|
||||
@@ -278,7 +672,7 @@ Machining::GetToolPreviewPrev( int nEntId, int nParentId, int nStId) const
|
||||
if ( m_pGeomDB->GetInfo( nNewParentId, KEY_DRACEX, vActExit))
|
||||
ActivateDrillingUnit( nStId, vActExit) ;
|
||||
}
|
||||
// recupero l'ultima entità del precedente gruppo
|
||||
// recupero l'ultima entità del precedente gruppo
|
||||
nNewId = m_pGeomDB->GetLastInGroup( nNewParentId) ;
|
||||
} while ( nNewId != GDB_ID_NULL) ;
|
||||
return GDB_ID_NULL ;
|
||||
@@ -286,9 +680,9 @@ Machining::GetToolPreviewPrev( int nEntId, int nParentId, int nStId) const
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int
|
||||
Machining::ToolPreview( int nEntId, int nStep) const
|
||||
Machining::ToolPreview( int nEntId, int nStep) /*const*/
|
||||
{
|
||||
// verifico validità gestori DB geometrico e CAM
|
||||
// verifico validità gestori DB geometrico e CAM
|
||||
if ( m_pGeomDB == nullptr || m_pMchMgr == nullptr)
|
||||
return GDB_ID_NULL ;
|
||||
// recupero la testa nel gruppo per anteprima utensile
|
||||
@@ -296,12 +690,16 @@ Machining::ToolPreview( int nEntId, int nStep) const
|
||||
if ( nStId == GDB_ID_NULL)
|
||||
return GDB_ID_NULL ;
|
||||
m_pGeomDB->SetStatus( nStId, GDB_ST_OFF) ;
|
||||
// recupero gruppo per geometria di lavorazione (Cutter Location)
|
||||
// recupero la macchina corrente
|
||||
Machine* pMch = m_pMchMgr->GetCurrMachine() ;
|
||||
if ( pMch == nullptr)
|
||||
return false ;
|
||||
// recupero il gruppo per geometria di lavorazione (Cutter Location)
|
||||
int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ;
|
||||
if ( nClId == GDB_ID_NULL)
|
||||
return GDB_ID_NULL ;
|
||||
int nParentId ;
|
||||
// se entità nulla
|
||||
// se entità nulla
|
||||
if ( nEntId == GDB_ID_NULL || ! m_pGeomDB->ExistsObj( nEntId)) {
|
||||
// recupero il gruppo di appartenenza
|
||||
nParentId = m_pGeomDB->GetFirstGroupInGroup( nClId) ;
|
||||
@@ -317,7 +715,7 @@ Machining::ToolPreview( int nEntId, int nStep) const
|
||||
}
|
||||
// altrimenti
|
||||
else {
|
||||
// verifico che l'entità stia in un sottogruppo di CL
|
||||
// verifico che l'entità stia in un sottogruppo di CL
|
||||
nParentId = m_pGeomDB->GetParentId( nEntId) ;
|
||||
if ( m_pGeomDB->GetParentId( nParentId) == nClId) {
|
||||
// se richiesta successiva
|
||||
@@ -351,7 +749,8 @@ Machining::ToolPreview( int nEntId, int nStep) const
|
||||
// se esiste il gruppo genitore, visualizzo testa preview
|
||||
if ( nParentId != GDB_ID_NULL)
|
||||
m_pGeomDB->SetStatus( nStId, GDB_ST_ON) ;
|
||||
// recupero i dati di questa entità
|
||||
|
||||
// recupero i dati dell'entità CamData corrrente
|
||||
const CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ;
|
||||
if ( pCamData == nullptr)
|
||||
return GDB_ID_NULL ;
|
||||
@@ -359,36 +758,77 @@ Machining::ToolPreview( int nEntId, int nStep) const
|
||||
AdjustEndPointForAxesCalc( pCamData, ptEnd) ;
|
||||
Vector3d vtTool = pCamData->GetToolDir() ;
|
||||
Vector3d vtBAux = pCamData->GetBackAuxDir() ;
|
||||
// dati correnti testa/uscita
|
||||
int nExitId = m_pGeomDB->GetFirstNameInGroup( nStId, MCH_EXIT + ToString( GetExitNbr())) ;
|
||||
Frame3d frExit ;
|
||||
m_pGeomDB->GetGroupGlobFrame( nExitId, frExit) ;
|
||||
// correggo eventuale movimento di disattivazione sempre in Z globale
|
||||
double dVal ;
|
||||
if ( m_pGeomDB->GetInfo( nExitId, MCH_EXIT_VAL, dVal))
|
||||
frExit.Translate( -dVal * Z_AX) ;
|
||||
Point3d ptOrig = frExit.Orig() ;
|
||||
Vector3d vtDir = frExit.VersZ() ;
|
||||
Vector3d vtAux ;
|
||||
int nAvId = m_pGeomDB->GetFirstNameInGroup( nStId, MCH_AUX_VECT) ;
|
||||
ExeStartVector( nAvId, GDB_ID_ROOT, vtAux) ;
|
||||
// rototraslo opportunamente
|
||||
Frame3d frHead ;
|
||||
m_pGeomDB->GetGroupGlobFrame( nStId, frHead) ;
|
||||
Frame3d frRef ;
|
||||
if ( vtAux.IsSmall() || AreSameOrOppositeVectorApprox( vtAux, vtDir))
|
||||
frRef.Set( ptOrig, vtDir) ;
|
||||
else
|
||||
frRef.Set( ptOrig, vtDir, vtAux) ;
|
||||
Frame3d frShow ;
|
||||
if ( vtBAux.IsSmall() || AreSameOrOppositeVectorApprox( vtBAux, vtTool))
|
||||
frShow.Set( ptEnd + vtTool * GetToolData().m_dLen, vtTool) ;
|
||||
else
|
||||
frShow.Set( ptEnd + vtTool * GetToolData().m_dLen, vtTool, vtBAux) ;
|
||||
frHead.ToLoc( frRef) ;
|
||||
frHead.ToGlob( frShow) ;
|
||||
*(m_pGeomDB->GetGroupFrame( nStId)) = frHead ;
|
||||
|
||||
// recupero gli assi e i loro valori per la posizione corrente
|
||||
const DBLVECTOR& vAxVal = pCamData->GetAxesVal() ;
|
||||
STRVECTOR vAxNames ; m_pMchMgr->GetAllCurrAxesNames( vAxNames) ;
|
||||
if ( ssize( vAxVal) != ssize( vAxNames))
|
||||
return GDB_ID_NULL ;
|
||||
int nAxSize = ssize( vAxVal) ;
|
||||
VPREVIEWAXISINFO vAxisInfo ; vAxisInfo.reserve( nAxSize) ;
|
||||
for ( int i = 0 ; i < nAxSize ; ++ i) {
|
||||
bool bLinear = false ;
|
||||
pMch->GetAxisType( vAxNames[i], bLinear) ;
|
||||
vAxisInfo.emplace_back( vAxNames[i], vAxVal[i], bLinear) ;
|
||||
}
|
||||
|
||||
// scorro gli assi dentro al gruppo "ST" ( vi sono solo quelli rotativi)
|
||||
for ( auto Iter = m_vPreviewAxisIds.begin() ; Iter != m_vPreviewAxisIds.end() ; ++ Iter) {
|
||||
// recupero l'Id dell'asse corrente
|
||||
int nAxisId = *Iter ;
|
||||
if ( nAxisId == GDB_ID_NULL)
|
||||
return GDB_ID_NULL ;
|
||||
// recupero il nome di tale asse
|
||||
string sAxisName ;
|
||||
if ( ! m_pGeomDB->GetName( nAxisId, sAxisName))
|
||||
return GDB_ID_NULL ;
|
||||
// recupero il valore corrente dell'asse
|
||||
double dCurrAxisVal = 0. ;
|
||||
m_pGeomDB->GetInfo( nAxisId, KEY_ROT_AXIS_VAL, dCurrAxisVal) ;
|
||||
// recupero il valore dell'asse
|
||||
bool bFound = false ;
|
||||
double dAxisVal = 0 ;
|
||||
for ( int i = 0 ; ! bFound && i < nAxSize ; ++ i) {
|
||||
if ( ! vAxisInfo[i].bLinear && EqualNoCase( sAxisName, vAxisInfo[i].sName)) {
|
||||
bFound = true ;
|
||||
dAxisVal = vAxisInfo[i].dVal ;
|
||||
}
|
||||
}
|
||||
if ( ! bFound)
|
||||
return GDB_ID_NULL ;
|
||||
// recupero il vettore dell'asse
|
||||
int nV = m_pGeomDB->GetFirstNameInGroup( nAxisId, sAxisName) ;
|
||||
const IGeoVector3d* pGV = GetGeoVector3d( m_pGeomDB->GetGeoObj( nV)) ;
|
||||
if ( pGV == nullptr)
|
||||
return GDB_ID_NULL ;
|
||||
Point3d ptPos = pGV->GetBase() ;
|
||||
Vector3d vtDir = pGV->GetVector() ;
|
||||
vtDir.Normalize() ;
|
||||
m_pGeomDB->RotateGroup( nAxisId, ptPos, vtDir, ( dAxisVal - dCurrAxisVal)) ;
|
||||
m_pGeomDB->SetInfo( nAxisId, KEY_ROT_AXIS_VAL, dAxisVal) ;
|
||||
}
|
||||
|
||||
// recupero la posizione del tip dell'utensile per movimento assi lineari
|
||||
if ( m_nPreviewToolTip == GDB_ID_NULL)
|
||||
return GDB_ID_NULL ;
|
||||
const IGeoPoint3d* pToolTip = GetGeoPoint3d( m_pGeomDB->GetGeoObj( m_nPreviewToolTip)) ;
|
||||
if ( pToolTip == nullptr)
|
||||
return GDB_ID_NULL ;
|
||||
Point3d ptToolTipLoc = pToolTip->GetPoint() ;
|
||||
Frame3d frGlob ; m_pGeomDB->GetGlobFrame( m_nPreviewToolTip, frGlob) ;
|
||||
Point3d ptToolTipGlob = GetToGlob( ptToolTipLoc, frGlob) ;
|
||||
Vector3d vtMoveGlob = ptEnd - ptToolTipGlob ;
|
||||
m_pGeomDB->GetGroupGlobFrame( nStId, frGlob) ;
|
||||
Vector3d vtMoveLoc = GetToLoc( vtMoveGlob, frGlob) ;
|
||||
/*IGeoPoint3d* A = CreateGeoPoint3d() ; A->Set( ptToolTipGlob) ;
|
||||
int B = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, A->Clone()) ;
|
||||
m_pGeomDB->SetMaterial( B, FUCHSIA) ;
|
||||
IGeoPoint3d* C = CreateGeoPoint3d() ; C->Set( ptEnd) ;
|
||||
int D = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, C->Clone()) ;
|
||||
m_pGeomDB->SetMaterial( D, YELLOW) ;*/
|
||||
m_pGeomDB->TranslateGroup( nStId, vtMoveLoc) ;
|
||||
|
||||
// ritorno l'Id dell'entità CamData corrente
|
||||
return nEntId ;
|
||||
}
|
||||
|
||||
@@ -440,7 +880,7 @@ Machining::CalcMirrorByDouble( int nClId, const string& sUserNotes)
|
||||
if ( nDouble == 0)
|
||||
return true ;
|
||||
|
||||
// assegno Id entità originali
|
||||
// assegno Id entità originali
|
||||
int nPathId = m_pGeomDB->GetFirstNameInGroup( nClId, MCH_PATH + "*") ;
|
||||
while ( nPathId != GDB_ID_NULL) {
|
||||
int nEntId = m_pGeomDB->GetFirstInGroup( nPathId) ;
|
||||
@@ -465,7 +905,7 @@ Machining::CalcMirrorByDouble( int nClId, const string& sUserNotes)
|
||||
m_pGeomDB->SetMaterial( nClPathId, GRAY) ;
|
||||
nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ;
|
||||
}
|
||||
// assegno Id entità double
|
||||
// assegno Id entità double
|
||||
nPathId = m_pGeomDB->GetFirstNameInGroup( nDblId, MCH_PATH + "*") ;
|
||||
while ( nPathId != GDB_ID_NULL) {
|
||||
int nEntId = m_pGeomDB->GetFirstInGroup( nPathId) ;
|
||||
|
||||
Reference in New Issue
Block a user