//---------------------------------------------------------------------------- // EgalTech 2015-2022 //---------------------------------------------------------------------------- // File : MachiningsMgr.cpp Data : 04.02.22 Versione : 2.4b1 // Contenuto : Implementazione gestore database lavorazioni. // // // // Modifiche : 02.06.15 DS Creazione modulo. // 13.07.16 DS Agg. gestione SplitArcs (MF_CURR_VER = 1005). // 02.03.17 DS Aggiunto controllo nome. // 17.05.17 DS Agg. gestione IntSawArcMaxSideAng (MF_CURR_VER = 1006). // 17.06.19 DS Agg. lavorazione finitura superficie (MF_CURR_VER = 1007). // 24.07.19 DS Agg. scrittura come versione letta e param MAXDEPTHSAFE (MF_CURR_VER = 1008). // 03.06.20 DS Agg. per nuovi parametri Tab in fresatura (MF_CURR_VER = 1009). // 22.06.20 DS Agg. per nuovi parametri attacco tagli di lama (MF_CURR_VER = 1010). // 09.11.20 DS Agg. per nuovi parametri tagli di lama (MF_CURR_VER = 1011). // 04.02.22 DS Agg. per nuovi parametri svuotature con epicicli (MF_CURR_VER = 1012). // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "MachiningsMgr.h" #include "MachiningDataFactory.h" #include "MachiningConst.h" #include "DllMain.h" #include "/EgtDev/Include/EGkGeoConst.h" #include "/EgtDev/Include/EMkMachiningConst.h" #include "/EgtDev/Include/EGnStringKeyVal.h" #include "/EgtDev/Include/EGnStringUtils.h" #include "/EgtDev/Include/EGnFileUtils.h" #include "/EgtDev/Include/EGnScanner.h" #include "/EgtDev/Include/EGnWriter.h" #include #include using namespace std ; //---------------------------------------------------------------------------- const string MF_HEADER = "[HEADER]" ; const string MF_VERSION = "VERSION" ; const string MF_TOTAL = "TOTAL" ; const string MF_SIZE = "SIZE" ; const int MF_CURR_VER = 1012 ; const string MF_GENERAL = "[GENERAL]" ; const string MF_3AXCOMP = "3AXCOMP" ; const bool MF_CURR_3AXCOMP = false ; const string MF_5AXCOMP = "5AXCOMP" ; const bool MF_CURR_5AXCOMP = false ; const string MF_SAFEZ = "SAFEZ" ; const double MF_CURR_SAFEZ = 100 ; const string MF_SAFEAGGRBOTTZ = "SAFEAGGRBOTTZ" ; const double MF_CURR_SAFEAGGRBOTTZ = 10 ; const string MF_EXTRALCR = "EXTRALCR" ; const double MF_CURR_EXTRALCR = 0 ; const string MF_EXTRARDR = "EXTRARDR" ; const double MF_CURR_EXTRARDR = 0 ; const string MF_HOLEDTOL = "HOLEDTOL" ; const double MF_CURR_HOLEDTOL = 10 * EPS_SMALL ; const string MF_EXTSAWARCMINRAD = "EXTSAWARCMINRAD" ; const double MF_CURR_EXTSAWARCMINRAD = 200 ; const string MF_INTSAWARCMAXSIDEANG = "INTSAWARCMAXSIDEANG" ; const double MF_CURR_INTSAWARCMAXSIDEANG = 45 ; const string MF_SPLITARCS = "SPLITARCS" ; const int MF_CURR_SPLITARCS = SPLAR_NEVER ; const string MF_MAXDEPTHSAFE = "MAXDEPTHSAFE" ; const double MF_CURR_MAXDEPTHSAFE = 2.0 ; //---------------------------------------------------------------------------- MachiningsMgr::MachiningsMgr( void) { m_pTsMgr = nullptr ; m_suCIter = m_suData.cend() ; m_pCurrMach = nullptr ; m_bModified = false ; m_nDbVer = 0 ; m_dSafeZ = MF_CURR_SAFEZ ; m_dSafeAggrBottZ = MF_CURR_SAFEAGGRBOTTZ ; m_b3AxComp = MF_CURR_3AXCOMP ; m_b5AxComp = MF_CURR_5AXCOMP ; m_dExtraLOnCutRegion = MF_CURR_EXTRALCR ; m_dExtraROnDrillRegion = MF_CURR_EXTRARDR ; m_dHoleDiamToler = MF_CURR_HOLEDTOL ; m_dExtSawArcMinRad = MF_CURR_EXTSAWARCMINRAD ; m_dIntSawArcMaxSideAng = MF_CURR_INTSAWARCMAXSIDEANG ; m_nSplitArcs = MF_CURR_SPLITARCS ; m_dMaxDepthSafe = MF_CURR_MAXDEPTHSAFE ; } //---------------------------------------------------------------------------- MachiningsMgr::~MachiningsMgr( void) { Clear( true) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Clear( bool bReset) { // libero la memoria dalle lavorazioni for ( auto iIter = m_umData.begin() ; iIter != m_umData.end() ; ++ iIter) { if ( iIter->second != nullptr) delete iIter->second ; } // reset puntatore a gestore utensili if ( bReset) m_pTsMgr = nullptr ; // pulisco le tabelle m_umData.clear() ; m_suData.clear() ; // reinizializzo stato m_suCIter = m_suData.cend() ; if ( m_pCurrMach != nullptr) { delete m_pCurrMach ; m_pCurrMach = nullptr ; } m_bModified = false ; return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Load( const string& sMachsFile, const ToolsMgr* pTsMgr) { // Salvo la path del file con i dati m_sMachsFile = sMachsFile ; string sOut = "MachiningsMgr Init : " + m_sMachsFile ; LOG_INFO( GetEMkLogger(), sOut.c_str()) // Verifico il gestore degli utensili if ( pTsMgr == nullptr) { LOG_ERROR( GetEMkLogger(), "LoadMachinings : Error on ToolsMgr") return false ; } m_pTsMgr = pTsMgr ; // carico return Reload() ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Reload( void) { // Pulisco Clear( false) ; // Verifico il gestore degli utensili if ( m_pTsMgr == nullptr) { LOG_ERROR( GetEMkLogger(), "ReloadMachinings : Error on ToolsMgr") return false ; } // Inizializzo lo scanner Scanner TheScanner ; if ( ! TheScanner.Init( m_sMachsFile, ";")) { LOG_ERROR( GetEMkLogger(), "ReloadMachinings : Error on Init") return false ; } // variabili di stato della lettura bool bOk = true ; bool bEnd = false ; // Leggo l'intestazione m_nDbVer = 0 ; int nTotal = 0 ; if ( ! LoadHeader( TheScanner, m_nDbVer, nTotal, bEnd)) { bOk = false ; string sOut = "ReloadMachinings : Error on Header" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } { string sOut = "ReloadMachinings : FileVer = " + ToString( m_nDbVer) + " CurrVer = " + ToString( MF_CURR_VER) ; LOG_DBG_INFO( GetEMkLogger(), sOut.c_str()) } // Leggo i dati generali (da versione 1002) if ( m_nDbVer >= 1002 && ! LoadGeneral( TheScanner, bEnd)) { bOk = false ; string sOut = "ReloadMachinings : Error on General" ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } // Dimensiono map const int MIN_BUCKETS = 163 ; m_umData.rehash( MIN_BUCKETS) ; // Ciclo di lettura delle lavorazioni do { PtrOwner pMch ; if ( LoadOneMachining( TheScanner, pMch, bEnd)) { // salvo i dati della lavorazione if ( ! IsNull( pMch)) { if ( ! m_umData.emplace( pMch->m_Uuid, Get( pMch)).second || ! m_suData.emplace( pMch->m_sName, pMch->m_Uuid).second) { bOk = false ; string sOut = "ReloadMachinings : Error adding machining " + pMch->m_sName ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } Release( pMch) ; } } else { bOk = false ; string sOut = "ReloadMachinings : Error on line " + ToString( TheScanner.GetCurrLineNbr()) ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) } } while ( bOk && ! bEnd) ; // Termino lo scanner TheScanner.Terminate() ; // Dichiaro versione corrente m_nDbVer = MF_CURR_VER ; return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::LoadHeader( Scanner& TheScanner, int& nVersion, int& nTotal, bool& bEnd) const { // leggo la prossima linea string sLine ; if ( ! TheScanner.GetLine( sLine)) { // fine file bEnd = true ; return true ; } // deve essere intestazione if ( sLine != MF_HEADER) return false ; bool bOk = true ; // leggo le linee successive bEnd = true ; nVersion = 1000 ; nTotal = 0 ; while ( bOk && TheScanner.GetLine( sLine)) { // se sezione successiva if ( sLine.front() == '[' && sLine.back() == ']') { TheScanner.UngetLine( sLine) ; bEnd = false ; break ; } // separo chiave da valore string sKey, sVal ; SplitFirst( sLine, "=", sKey, sVal) ; // riconosco la chiave if ( ToUpper( sKey) == MF_VERSION) bOk = FromString( sVal, nVersion) ; else if ( ToUpper( sKey) == MF_TOTAL) bOk = FromString( sVal, nTotal) ; else bOk = false ; } // controllo il numero di versione nVersion = min( nVersion, MF_CURR_VER) ; return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::LoadGeneral( Scanner& TheScanner, bool& bEnd) { // leggo la prossima linea string sLine ; if ( ! TheScanner.GetLine( sLine)) { // fine file bEnd = true ; return true ; } // deve essere dati generali if ( sLine != MF_GENERAL) return false ; bool bOk = true ; bool bSafeAggrBottZ = false ; // leggo le linee successive bEnd = true ; while ( bOk && TheScanner.GetLine( sLine)) { // se sezione successiva if ( sLine.front() == '[' && sLine.back() == ']') { TheScanner.UngetLine( sLine) ; bEnd = false ; break ; } // separo chiave da valore string sKey, sVal ; SplitFirst( sLine, "=", sKey, sVal) ; // riconosco la chiave if ( ToUpper( sKey) == MF_3AXCOMP) bOk = FromString( sVal, m_b3AxComp) ; else if ( ToUpper( sKey) == MF_5AXCOMP) bOk = FromString( sVal, m_b5AxComp) ; else if ( ToUpper( sKey) == MF_SAFEZ) bOk = FromString( sVal, m_dSafeZ) ; else if ( ToUpper( sKey) == MF_SAFEAGGRBOTTZ) { bSafeAggrBottZ = true ; bOk = FromString( sVal, m_dSafeAggrBottZ) ; } else if ( ToUpper( sKey) == MF_EXTRALCR) bOk = FromString( sVal, m_dExtraLOnCutRegion) ; else if ( ToUpper( sKey) == MF_EXTRARDR) bOk = FromString( sVal, m_dExtraROnDrillRegion) ; else if ( ToUpper( sKey) == MF_HOLEDTOL) bOk = FromString( sVal, m_dHoleDiamToler) ; else if ( ToUpper( sKey) == MF_EXTSAWARCMINRAD) bOk = FromString( sVal, m_dExtSawArcMinRad) ; else if ( ToUpper( sKey) == MF_INTSAWARCMAXSIDEANG) bOk = FromString( sVal, m_dIntSawArcMaxSideAng) ; else if ( ToUpper( sKey) == MF_SPLITARCS) bOk = FromString( sVal, m_nSplitArcs) ; else if ( ToUpper( sKey) == MF_MAXDEPTHSAFE) bOk = FromString( sVal, m_dMaxDepthSafe) ; else bOk = true ; } if ( ! bSafeAggrBottZ) m_dSafeAggrBottZ = m_dSafeZ ; return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::LoadOneMachining( Scanner& TheScanner, PtrOwner& pMch, bool& bEnd) const { // leggo la prossima linea string sLine ; if ( ! TheScanner.GetLine( sLine)) { pMch.Reset() ; // fine file bEnd = true ; return true ; } // deve essere intestazione if ( sLine.front() != '[' || sLine.back() != ']') return false ; Trim( sLine, "[]") ; // separo tipo da contatore string sType, sCount ; SplitFirst( sLine, "_", sType, sCount) ; ToUpper( sType) ; // alloco la lavorazione del tipo corrispondente pMch.Set( MCHDATA_CREATE( MCHDATA_NAMETOTYPE( sType))) ; if ( IsNull( pMch)) return false ; bool bOk = true ; // imposto dimensione di default int nSize = 0 ; switch ( pMch->GetType()) { case MT_DRILLING : nSize = 11 ; break ; case MT_SAWING : nSize = 15 ; break ; case MT_MILLING : nSize = 26 ; break ; } // eventuale lettura da file if ( TheScanner.GetLine( sLine)) { if ( ! GetVal( sLine, MF_SIZE, nSize)) TheScanner.UngetLine( sLine) ; } // leggo i dati della lavorazione bEnd = true ; const int DIM_BS = 64 ; bitset Flag ; assert( nSize <= DIM_BS) ; while ( bOk && TheScanner.GetLine( sLine)) { // se lavorazione successiva if ( sLine.front() == '[' && sLine.back() == ']') { TheScanner.UngetLine( sLine) ; bEnd = false ; break ; } // interpreto la linea int nKey = - 1 ; bOk = pMch->FromString( sLine, nKey) ; // se tutto bene, dichiaro letto il campo if ( bOk) Flag.set( nKey) ; } // verifico di aver letto tutti i campi bOk = bOk && ( Flag.count() == nSize) ; return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Save( bool bCompressed) const { // Se non ci sono state modifiche, esco subito if ( ! m_bModified) return true ; // Faccio copia di backup del file originale CopyFileEgt( m_sMachsFile, m_sMachsFile + ".bak") ; // Inizializzo il writer Writer TheWriter ; if ( ! TheWriter.Init( m_sMachsFile, bCompressed)) { LOG_ERROR( GetEMkLogger(), "SaveMachinings : Error on Init") return false ; } // Scrivo linea di inizio file string sOut = "; --- " + m_sMachsFile + " " + CurrDateTime() + " ---" ; if ( ! TheWriter.OutText( sOut)) { LOG_ERROR( GetEMkLogger(), "SaveMachinings : Error on Start") return false ; } // Scrivo l'intestazione if ( ! SaveHeader( TheWriter)) { LOG_ERROR( GetEMkLogger(), "SaveMachinings : Error on Header") return false ; } // Scrivo i dati generali if ( ! SaveGeneral( TheWriter)) { LOG_ERROR( GetEMkLogger(), "SaveMachinings : Error on General") return false ; } // Ciclo su tutti i nomi delle lavorazioni int nCounter = 0 ; for ( auto iIter = m_suData.cbegin() ; iIter != m_suData.cend() ; ++ iIter) { // salvo l'utensile if ( ! SaveOneMachining( iIter->second, nCounter, TheWriter)) { string sOut = "SaveMachinings : Error on machining " + iIter->first ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) return false ; } } // Scrivo linea di fine file if ( ! TheWriter.OutText( "; --- End ---")) { LOG_ERROR( GetEMkLogger(), "SaveMachinings : Error on End") return false ; } // Chiudo la scrittura TheWriter.Close() ; // Dichiaro non più modificato rispetto al file m_bModified = false ; return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SaveHeader( Writer& TheWriter, int nTotal) const { // scrivo l'intestazione bool bOk = true ; string sOut ; sOut = MF_HEADER ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_VERSION + "=" + ToString( m_nDbVer) ; bOk = bOk && TheWriter.OutText( sOut) ; if ( nTotal == -1) sOut = MF_TOTAL + "=" + ToString( int( m_umData.size())) ; else sOut = MF_TOTAL + "=" + ToString( nTotal) ; bOk = bOk && TheWriter.OutText( sOut) ; return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SaveGeneral( Writer& TheWriter) const { // scrivo l'intestazione bool bOk = true ; string sOut ; sOut = MF_GENERAL ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_3AXCOMP + "=" + ToString( m_b3AxComp) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_5AXCOMP + "=" + ToString( m_b5AxComp) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_SAFEZ + "=" + ToString( m_dSafeZ) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_SAFEAGGRBOTTZ + "=" + ToString( m_dSafeAggrBottZ) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_EXTRALCR + "=" + ToString( m_dExtraLOnCutRegion) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_EXTRARDR + "=" + ToString( m_dExtraROnDrillRegion) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_HOLEDTOL + "=" + ToString( m_dHoleDiamToler) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_EXTSAWARCMINRAD + "=" + ToString( m_dExtSawArcMinRad) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_INTSAWARCMAXSIDEANG + "=" + ToString( m_dIntSawArcMaxSideAng) ; bOk = bOk && TheWriter.OutText( sOut) ; sOut = MF_SPLITARCS + "=" + ToString( m_nSplitArcs) ; bOk = bOk && TheWriter.OutText( sOut) ; if ( m_nDbVer >= 1008) { sOut = MF_MAXDEPTHSAFE + "=" + ToString( m_dMaxDepthSafe) ; bOk = bOk && TheWriter.OutText( sOut) ; } return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SaveOneMachining( const EgtUUID& Uuid, int& nCounter, Writer& TheWriter) const { // recupero i dati della lavorazione auto iIter = m_umData.find( Uuid) ; if ( iIter == m_umData.end()) return false ; const MachiningData* pmData = iIter->second ; // preparo la lista dei dati (quelli vuoti sono opzionali con default) STRVECTOR vsOut ; for ( int i = 0 ; i < pmData->GetSize() ; ++ i) { string sOut = pmData->ToString( i) ; if ( ! sOut.empty()) vsOut.emplace_back( sOut) ; } // scrivo i dati della lavorazione string sOut = "[" + pmData->GetTitle() + "_" + ToString( ++ nCounter, 3) + "]" ; bool bOk = TheWriter.OutText( sOut) ; sOut = MF_SIZE + "=" + ToString( int( vsOut.size())) ; bOk = bOk && TheWriter.OutText( sOut) ; for ( const auto& sOut : vsOut) { bOk = bOk && TheWriter.OutText( sOut) ; } return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::GetMachiningNewName( string& sName) const { // il parametro nome deve essere valido if ( &sName == nullptr) return false ; // se nome vuoto, assegno radice standard if ( sName.empty()) sName = "Mach" ; // verifico che il nome sia unico int nCount = 0 ; string sOrigName = sName ; if ( sOrigName.length() > 2 && sOrigName.rfind( "_1") == sOrigName.length() - 2) sOrigName.erase( sOrigName.length() - 2) ; while ( GetMachining( sName) != nullptr) { ++ nCount ; sName = sOrigName + "_" + ToString( nCount) ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::AddMachining( const string& sName, int nType) { // annullo lavorazione corrente if ( m_pCurrMach != nullptr) { delete m_pCurrMach ; m_pCurrMach = nullptr ; } // verifico validità del nome if ( ! IsValidVal( sName)) return false ; // verifico unicità del nome if ( m_suData.find( sName) != m_suData.end()) return false ; // verifico validità del tipo if ( ! IsValidMachiningType( nType)) return false ; // alloco la lavorazione del tipo corrispondente PtrOwner pMch( MCHDATA_CREATE( nType)) ; if ( IsNull( pMch)) return false ; bool bOk = true ; // assegno nome pMch->m_sName = sName ; CreateEgtUUID( pMch->m_Uuid) ; // salvo i dati della lavorazione bOk = bOk && m_umData.emplace( pMch->m_Uuid, Get( pMch)).second ; bOk = bOk && m_suData.emplace( pMch->m_sName, pMch->m_Uuid).second ; m_bModified = true ; // la rendo la nuova lavorazione corrente m_pCurrMach = pMch->Clone() ; Release( pMch) ; // decodifico i parametri speciali DecodeSpecialParams( m_pCurrMach) ; return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::CopyMachining( const string& sSource, const string& sName) { // annullo lavorazione corrente if ( m_pCurrMach != nullptr) { delete m_pCurrMach ; m_pCurrMach = nullptr ; } // verifico validità del nome if ( ! IsValidVal( sName)) return false ; // verifico unicità del nome if ( m_suData.find( sName) != m_suData.end()) return false ; // recupero la lavorazione sorgente const MachiningData* pMdata = GetMachining( sSource) ; if ( pMdata == nullptr) return false ; // ne faccio una copia PtrOwner pMch( pMdata->Clone()) ; if ( IsNull( pMch)) return false ; bool bOk = true ; // modifico nome e UUID pMch->m_sName = sName ; CreateEgtUUID( pMch->m_Uuid) ; // salvo i dati della lavorazione bOk = bOk && m_umData.emplace( pMch->m_Uuid, Get( pMch)).second ; bOk = bOk && m_suData.emplace( pMch->m_sName, pMch->m_Uuid).second ; m_bModified = true ; // la rendo la nuova lavorazione corrente m_pCurrMach = pMch->Clone() ; Release( pMch) ; // decodifico i parametri speciali DecodeSpecialParams( m_pCurrMach) ; return bOk ; } //---------------------------------------------------------------------------- bool MachiningsMgr::RemoveMachining( const string& sName) { // cerco la lavorazione nell'elenco dei nomi e degli UUID auto iNameIter = m_suData.find( sName) ; if ( iNameIter == m_suData.end()) return true ; auto iIter = m_umData.find( iNameIter->second) ; // se era anche la lavorazione corrente, la resetto if ( m_pCurrMach != nullptr && m_pCurrMach->m_Uuid == iNameIter->second) { delete m_pCurrMach ; m_pCurrMach = nullptr ; } if ( iIter != m_umData.end()) { // libero la memoria della lavorazione delete iIter->second ; // rimuovo la lavorazione dal dizionario degli UUID m_umData.erase( iIter) ; } // rimuovo la lavorazione dall'elenco dei nomi m_suData.erase( iNameIter) ; // dichiaro la modifica m_bModified = true ; return true ; } //---------------------------------------------------------------------------- const MachiningData* MachiningsMgr::GetMachining( const EgtUUID& Uuid) const { auto iIter = m_umData.find( Uuid) ; if ( iIter == m_umData.end()) return nullptr ; return iIter->second ; } //---------------------------------------------------------------------------- const MachiningData* MachiningsMgr::GetMachining( const string& sName) const { auto iIter = m_suData.find( sName) ; if ( iIter == m_suData.end()) return nullptr ; return GetMachining( iIter->second) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::GetFirstMachining( int nType, string& sName) const { // recupero primo nome m_suCIter = m_suData.begin() ; // lo verifico if ( VerifyCurrMachining( nType, sName)) return true ; // continuo ricerca return GetNextMachining( nType, sName) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::GetNextMachining( int nType, string& sName) const { // mentre esiste lavorazione while ( m_suCIter != m_suData.end()) { // nome successivo ++ m_suCIter ; // lo verifico if ( VerifyCurrMachining( nType, sName)) return true ; } // non trovato return false ; } //---------------------------------------------------------------------------- bool MachiningsMgr::VerifyCurrMachining( int nType, string& sName) const { // il corrente deve esistere if ( m_suCIter == m_suData.end()) return false ; // relativa lavorazione auto iIter = m_umData.find( m_suCIter->second) ; if ( iIter == m_umData.end()) { m_suCIter = m_suData.end() ; return false ; } // se è del tipo richiesto, trovato if ( iIter->second->GetType() == nType) { sName = iIter->second->m_sName ; return true ; } return false ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetCurrMachining( const string& sName) { // se c'è lavorazione corrente, la elimino if ( m_pCurrMach != nullptr) { delete m_pCurrMach ; m_pCurrMach = nullptr ; } // recupero i dati della lavorazione const MachiningData* pMdata = GetMachining( sName) ; if ( pMdata == nullptr) return false ; // li copio come correnti m_pCurrMach = pMdata->Clone() ; if ( m_pCurrMach == nullptr) return false ; // decodifico i parametri speciali return DecodeSpecialParams( m_pCurrMach) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SaveCurrMachining( void) { // verifico validità lavorazione corrente if ( m_pCurrMach == nullptr) return false ; // recupero puntatore a lavorazione corrente nel DB auto iIter = m_umData.find( m_pCurrMach->m_Uuid) ; if ( iIter == m_umData.end()) return false ; // la lavorazione corrente e la sua origine nel DB devono essere dello stesso tipo if ( m_pCurrMach->GetType() != iIter->second->GetType()) return false ; // se cambiato nome, devo aggiornare tabella relativa if ( m_pCurrMach->m_sName != iIter->second->m_sName) { // cerco la lavorazione nell'elenco dei nomi auto iNameIter = m_suData.find( iIter->second->m_sName) ; if ( iNameIter != m_suData.end()) { // rimuovo vecchio nome m_suData.erase( iNameIter) ; // inserisco nuovo m_suData.emplace( m_pCurrMach->m_sName, m_pCurrMach->m_Uuid) ; } } // eseguo salvataggio m_bModified = true ; iIter->second->CopyFrom( m_pCurrMach) ; // codifico i parametri speciali EncodeSpecialParams( iIter->second) ; return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::IsCurrMachiningModified( void) const { // verifico validità lavorazione corrente if ( m_pCurrMach == nullptr) return false ; // recupero puntatore a lavorazione corrente nel DB auto iIter = m_umData.find( m_pCurrMach->m_Uuid) ; if ( iIter == m_umData.end()) return false ; // codifico una copia della lavorazione corrente PtrOwner pMch( m_pCurrMach->Clone()) ; EncodeSpecialParams( pMch) ; // eseguo confronto return ( ! SameMachining( pMch, iIter->second)) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetCurrMachiningParam( int nType, bool bVal) { return ( ( m_pCurrMach != nullptr) ? m_pCurrMach->SetParam( nType, bVal) : false) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetCurrMachiningParam( int nType, int nVal) { return ( ( m_pCurrMach != nullptr) ? m_pCurrMach->SetParam( nType, nVal) : false) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetCurrMachiningParam( int nType, double dVal) { // deve esistere lavorazione corrente if ( m_pCurrMach == nullptr) return false ; // se speed if ( nType == MPA_SPEED) { // recupero valore da utensile const ToolData* pTdata ; if ( ! m_pCurrMach->GetTool( m_pTsMgr, pTdata)) return false ; // verifico non superi il massimo consentito if ( abs( dVal) > pTdata->m_dMaxSpeed + EPS_SMALL) return false ; // verifico se valore coincide con impostazione da utensile double dTVal ; if ( pTdata->GetParam( MPA2TPA( nType), dTVal) && AreSameAngValue( dVal, dTVal)) dVal = 0 ; return m_pCurrMach->SetParam( nType, dVal) ; } // se feed else if ( nType == MPA_FEED || nType == MPA_STARTFEED || nType == MPA_ENDFEED || nType == MPA_TIPFEED || nType == MPA_VERTFEED || nType == MPA_BACKFEED) { // recupero valore da utensile const ToolData* pTdata ; if ( ! m_pCurrMach->GetTool( m_pTsMgr, pTdata)) return false ; // verifico se valore coincide con impostazione da utensile double dTVal ; if ( pTdata->GetParam( MPA2TPA( nType), dTVal) && AreSameLenValue( dVal, dTVal)) dVal = 0 ; return m_pCurrMach->SetParam( nType, dVal) ; } // se sovramateriale else if ( nType == MPA_OFFSR || nType == MPA_OFFSL) { // recupero valore da utensile const ToolData* pTdata ; if ( ! m_pCurrMach->GetTool( m_pTsMgr, pTdata)) return false ; // verifico se valore coincide con impostazione da utensile double dTVal ; if ( pTdata->GetParam( MPA2TPA( nType), dTVal) && AreSameLenValue( dVal, dTVal)) dVal = UNKNOWN_PAR ; return m_pCurrMach->SetParam( nType, dVal) ; } // in generale return m_pCurrMach->SetParam( nType, dVal) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::DecodeSpecialParams( MachiningData* pMach) const { if ( pMach == nullptr) return false ; double dStep ; if ( pMach->GetParam( MPA_STEP, dStep)) { double dStepBack ; if ( pMach->GetParam( MPA_STEPBACK, dStepBack) && IsUnknownValue( dStepBack)) pMach->SetParam( MPA_STEPBACK, dStep) ; double dStepLast ; if ( pMach->GetParam( MPA_STEPLAST, dStepLast) && IsUnknownValue( dStepLast)) pMach->SetParam( MPA_STEPLAST, dStep) ; double dStepSideAng ; if ( pMach->GetParam( MPA_STEPSIDEANG, dStepSideAng)) { if ( IsUnknownValue( dStepSideAng)) { dStepSideAng = dStep ; pMach->SetParam( MPA_STEPSIDEANG, dStep) ; } double dStepSideAngBack ; if ( pMach->GetParam( MPA_STEPSIDEANGBACK, dStepSideAngBack) && IsUnknownValue( dStepSideAngBack)) pMach->SetParam( MPA_STEPSIDEANGBACK, dStepSideAng) ; } } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::EncodeSpecialParams( MachiningData* pMach) const { if ( pMach == nullptr) return false ; double dStep ; if ( pMach->GetParam( MPA_STEP, dStep)) { double dStepBack ; if ( pMach->GetParam( MPA_STEPBACK, dStepBack) && AreSameLenValue( dStepBack, dStep)) pMach->SetParam( MPA_STEPBACK, UNKNOWN_PAR) ; double dStepSideAng ; if ( pMach->GetParam( MPA_STEPSIDEANG, dStepSideAng)) { if ( AreSameLenValue( dStepSideAng, dStep)) pMach->SetParam( MPA_STEPSIDEANG, UNKNOWN_PAR) ; double dStepSideAngBack ; if ( pMach->GetParam( MPA_STEPSIDEANGBACK, dStepSideAngBack) && AreSameLenValue( dStepSideAngBack, dStepSideAng)) pMach->SetParam( MPA_STEPSIDEANGBACK, UNKNOWN_PAR) ; } } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetCurrMachiningParam( int nType, const string& sVal) { // deve esistere lavorazione corrente if ( m_pCurrMach == nullptr) return false ; // non è possibile cambiare UUID e Tool UUID if ( nType == MPA_UUID || nType == MPA_TUUID) return false ; // è possibile cambiare il nome, solo se il nuovo è valido e non è già presente nel DB (esclusa lavorazione corrente) if ( nType == MPA_NAME) { if ( ! IsValidVal( sVal)) return false ; const MachiningData* pMch = GetMachining( sVal) ; if ( pMch != nullptr && pMch->m_Uuid != m_pCurrMach->m_Uuid) return false ; } // è possibile cambiare l'utensile, solo se esiste o è nullo else if ( nType == MPA_TOOL) { // se nome vuoto, voglio cancellare l'utensile if ( sVal.empty()) return m_pCurrMach->ResetTool() ; // verifico utensile e assegno const ToolData* pTdata ; if ( ! m_pCurrMach->VerifyTool( m_pTsMgr, sVal, pTdata)) return false ; return ( m_pCurrMach->SetParam( MPA_TOOL, sVal) && m_pCurrMach->SetParam( MPA_TUUID, ::ToString( pTdata->m_Uuid))) ; } // eseguo return m_pCurrMach->SetParam( nType, sVal) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::GetCurrMachiningParam( int nType, bool& bVal) const { return ( ( m_pCurrMach != nullptr) ? m_pCurrMach->GetParam( nType, bVal) : false) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::GetCurrMachiningParam( int nType, int& nVal) const { return ( ( m_pCurrMach != nullptr) ? m_pCurrMach->GetParam( nType, nVal) : false) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::GetCurrMachiningParam( int nType, double& dVal) const { // deve esistere lavorazione corrente if ( m_pCurrMach == nullptr) return false ; // se speed if ( nType == MPA_SPEED) { // recupero il valore if ( ! m_pCurrMach->GetParam( nType, dVal)) return false ; // se valore autonomo if ( ! IsNullAngValue( dVal)) return true ; // devo recuperare valore da utensile const ToolData* pTdata ; if ( ! m_pCurrMach->GetTool( m_pTsMgr, pTdata)) return false ; // recupero il corretto valore return pTdata->GetParam( MPA2TPA( nType), dVal) ; } // se feed else if ( nType == MPA_FEED || nType == MPA_STARTFEED || nType == MPA_ENDFEED || nType == MPA_TIPFEED || nType == MPA_VERTFEED || nType == MPA_BACKFEED || nType == MPA_SIDEANGFEED) { // recupero il valore if ( ! m_pCurrMach->GetParam( nType, dVal)) return false ; // se valore autonomo if ( ! IsNullLenValue( dVal)) return true ; // devo recuperare valore da utensile const ToolData* pTdata ; if ( ! m_pCurrMach->GetTool( m_pTsMgr, pTdata)) return false ; // recupero il corretto valore return pTdata->GetParam( MPA2TPA( nType), dVal) ; } // se sovramateriale else if ( nType == MPA_OFFSR || nType == MPA_OFFSL) { // recupero il valore if ( ! m_pCurrMach->GetParam( nType, dVal)) return false ; // se valore autonomo if ( ! IsUnknownValue( dVal)) return true ; // devo recuperare valore da utensile dVal = 0 ; const ToolData* pTdata ; if ( ! m_pCurrMach->GetTool( m_pTsMgr, pTdata)) return false ; // recupero il corretto valore return pTdata->GetParam( MPA2TPA( nType), dVal) ; } // in generale return m_pCurrMach->GetParam( nType, dVal) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::GetCurrMachiningParam( int nType, string& sVal) const { return ( ( m_pCurrMach != nullptr) ? m_pCurrMach->GetParam( nType, sVal) : false) ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Set3AxComp( bool b3AxComp) { // se cambiato, salvo e setto modifica if ( b3AxComp != m_b3AxComp) { m_b3AxComp = b3AxComp ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Set5AxComp( bool b5AxComp) { // se cambiato, salvo e setto modifica if ( b5AxComp != m_b5AxComp) { m_b5AxComp = b5AxComp ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetSafeZ( double dSafeZ) { // deve essere un valore positivo o nullo if ( dSafeZ < - EPS_SMALL) return false ; // se cambiato, salvo e setto modifica if ( abs( dSafeZ - m_dSafeZ) > EPS_SMALL) { m_dSafeZ = dSafeZ ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetSafeAggrBottZ( double dSafeAggrBottZ) { // deve essere un valore positivo o nullo if ( dSafeAggrBottZ < - EPS_SMALL) return false ; // se cambiato, salvo e setto modifica if ( abs( dSafeAggrBottZ - m_dSafeAggrBottZ) > EPS_SMALL) { m_dSafeAggrBottZ = dSafeAggrBottZ ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetExtraLOnCutRegion( double dExtraL) { // deve essere un valore positivo o nullo if ( dExtraL < - EPS_SMALL) return false ; // se cambiato, salvo e setto modifica if ( abs( dExtraL - m_dExtraLOnCutRegion) > EPS_SMALL) { m_dExtraLOnCutRegion = dExtraL ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetExtraROnDrillRegion( double dExtraR) { // deve essere un valore positivo o nullo if ( dExtraR < - EPS_SMALL) return false ; // se cambiato, salvo e setto modifica if ( abs( dExtraR - m_dExtraROnDrillRegion) > EPS_SMALL) { m_dExtraROnDrillRegion = dExtraR ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetHoleDiamToler( double dToler) { // Tolleranza > 0 -> la punta può essere uguale, più piccola o più grande // Tolleranza < 0 -> la punta può essere solo uguale o più piccola // minimo valore assoluto EPS_SMALL if ( abs( dToler) < EPS_SMALL) dToler = EPS_SMALL ; // se cambiato, salvo e setto modifica if ( abs( dToler - m_dHoleDiamToler) > EPS_SMALL) { m_dHoleDiamToler = dToler ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetExtSawArcMinRad( double dRad) { // deve essere un valore positivo o nullo if ( dRad < - EPS_SMALL) return false ; // se cambiato, salvo e setto modifica if ( abs( dRad - m_dExtSawArcMinRad) > EPS_SMALL) { m_dExtSawArcMinRad = dRad ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetIntSawArcMaxSideAng( double dSideAng) { // deve essere un valore positivo e minore o uguale al limite const double MAX_SIDE_ANG = 60 ; if ( dSideAng < - EPS_ANG_SMALL || dSideAng > MAX_SIDE_ANG + EPS_ANG_SMALL) return false ; // se cambiato, salvo e setto modifica if ( abs( dSideAng - m_dIntSawArcMaxSideAng) > EPS_ANG_SMALL) { m_dIntSawArcMaxSideAng = dSideAng ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetSplitArcs( int nFlag) { if ( nFlag < SPLAR_NEVER || nFlag > SPLAR_ALWAYS) return false ; // se cambiato, salvo e setto modifica if ( nFlag != m_nSplitArcs) { m_nSplitArcs = nFlag ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SetMaxDepthSafe( double dSafe) { // verifico non sia inferiore al minimo dSafe = max( dSafe, MF_CURR_MAXDEPTHSAFE) ; // se cambiato, salvo e setto modifica if ( abs( dSafe - m_dMaxDepthSafe) > EPS_SMALL) { m_dMaxDepthSafe = dSafe ; m_bModified = true ; } return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Export( const STRVECTOR& vsMachiningsNames, const string& sOutFile, bool bCompressed) const { LOG_INFO( GetEMkLogger(), ( "Export Machinings " + sOutFile).c_str()) ; // conto le lavorazioni da esportare int nMachinings = 0 ; for ( size_t i = 0 ; i < vsMachiningsNames.size() ; i ++) { if ( m_suData.find( vsMachiningsNames[i]) != m_suData.end()) nMachinings ++ ; } // inizializzo il writer Writer TheWriter ; if ( ! TheWriter.Init( sOutFile, bCompressed)) { LOG_ERROR( GetEMkLogger(), " Error Exporting Machinings on Init") ; return false ; } // scrivo linea di inizio file string sOut = "; --- " + sOutFile + " " + CurrDateTime() + " ---" ; if ( ! TheWriter.OutText( sOut)) { LOG_ERROR( GetEMkLogger(), " Error Exporting Machinings on Start") ; return false ; } // scrivo l'intestazione if ( ! SaveHeader( TheWriter, nMachinings)) { LOG_ERROR( GetEMkLogger(), " Error Exporting Machinings on Header") ; return false ; } // scrivo i dati generali if ( m_nDbVer >= 1002 && ! SaveGeneral( TheWriter)) { LOG_ERROR( GetEMkLogger(), " Error Exporting Machinings on General") ; return false ; } // ciclo su tutti i nomi delle lavorazioni da esportare int nCounter = 0 ; for ( size_t i = 0 ; i < vsMachiningsNames.size() ; i ++) { auto it = m_suData.find( vsMachiningsNames[i]) ; // se la lavorazione non esiste passo alla sucessiva if ( it == m_suData.end()) { string sOut = " Warning Exporting Machinings : " + vsMachiningsNames[i] + " not found. Machining is ignored." ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) ; continue ; } // esporto la singola lavorazione if ( ! SaveOneMachining( it->second, nCounter, TheWriter)) { string sOut = " Error Exporting Machining " + it->first ; LOG_ERROR( GetEMkLogger(), sOut.c_str()) ; return false ; } } // scrivo linea di fine file if ( ! TheWriter.OutText( "; --- End ---")) { LOG_ERROR( GetEMkLogger(), " Error Exporting Machinings on End") ; return false ; } // chiudo la scrittura TheWriter.Close() ; return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::ToBeImported( const string& sFile, STRVECTOR& vsMachiningsNames, INTVECTOR& vMachiningsTypes) const { // inizializzo lo scanner Scanner TheScanner ; if ( ! TheScanner.Init( sFile, ";")) { LOG_ERROR( GetEMkLogger(), "Machinings ToBeImported : Error on Init") ; return false ; } // leggo l'intestazione int nVersion = 0 ; int nTotal = 0 ; bool bEnd = false ; if ( ! LoadHeader( TheScanner, nVersion, nTotal, bEnd)) { LOG_ERROR( GetEMkLogger(), "Machinings ToBeImported : Error on Header") ; return false ; } string sLine ; if ( ! TheScanner.GetLine( sLine)) return true ; // il file è finito, esco // parte generale if ( sLine == MF_GENERAL) { // scorro le linee fino ad arrivare alla sezione successiva while ( TheScanner.GetLine( sLine)) { if ( sLine.front() == '[' && sLine.back() == ']') break ; } // se non c'è una sezione successiva esco if ( sLine.empty()) return true ; } else if ( nVersion >= 1002) { LOG_ERROR( GetEMkLogger(), "Machinings ToBeImported : General section missing") ; return false ; } bool bMachiningName = false ; bool bAdd = true ; string sName ; int nType = 0 ; TheScanner.UngetLine( sLine) ; while ( TheScanner.GetLine( sLine)) { if ( sLine.front() == '[' && sLine.back() == ']') { // se è intestazione // aggiungo lavorazione precedente se ok if ( bAdd && bMachiningName && nType != 0) { vsMachiningsNames.push_back( sName) ; vMachiningsTypes.push_back( nType) ; } bMachiningName = false ; bAdd = true ; // salvo il tipo di lavorazione Trim( sLine, "[]") ; string sType, sCount ; SplitFirst( sLine, "_", sType, sCount) ; ToUpper( sType) ; nType = MCHDATA_NAMETOTYPE( sType) ; } else { string sKey, sVal ; SplitFirst( sLine, "=", sKey, sVal) ; if ( ToUpper( sKey) == "NAME") { if ( ! bMachiningName) { bMachiningName = true ; sName = sVal ; } else // se lavorazione ha più nomi è errore, non deve essere importata bAdd = false ; } } } // aggiungo ultima lavorazione se ok if ( bAdd && bMachiningName && nType != 0) { vsMachiningsNames.push_back( sName) ; vMachiningsTypes.push_back( nType) ; } if ( vsMachiningsNames.size() != vMachiningsTypes.size()) return false ; return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::Import( const string& sFile, const STRVECTOR& vsMachiningsToImport, const STRVECTOR& vsMachiningsNames, STRVECTOR& vsImported) { LOG_INFO( GetEMkLogger(), ( "Import Machinings : " + sFile).c_str()) ; for ( size_t i = 0 ; i < vsMachiningsNames.size() ; i ++) { // se esiste già nel DB una lavorazione con lo stesso nome la rimuovo if ( m_suData.find( vsMachiningsNames[i]) != m_suData.end()) { bool bOk = RemoveMachining( vsMachiningsNames[i]) ; if ( ! bOk) { LOG_ERROR( GetEMkLogger(), ( " Error Importing Machinings : removing " + vsMachiningsNames[i] + " failed" ).c_str()) ; return false ; } } // verfico che il nome non sia ripetuto nella lista dei vsMachiningsNames if ( i != vsMachiningsNames.size() - 1 && find( vsMachiningsNames.begin() + i + 1, vsMachiningsNames.end(), vsMachiningsNames[i]) != vsMachiningsNames.end()) { LOG_ERROR( GetEMkLogger(), ( " Error Importing Machinings : name \"" + vsMachiningsNames[i] + "\" is already used").c_str()) ; return false ; } } // inizializzo lo scanner Scanner TheScanner ; if ( ! TheScanner.Init( sFile, ";")) { LOG_ERROR( GetEMkLogger(), " Error Importing Machinings on Init") ; return false ; } // leggo l'intestazione int nVersion = 0 ; int nTotal = 0 ; bool bEnd ; if ( ! LoadHeader( TheScanner, nVersion, nTotal, bEnd)) { LOG_ERROR( GetEMkLogger(), " Error Importing Machinings on Header") ; return false ; } // leggo i dati generali (da versione 1002) e li ignoro if ( nVersion >= 1002 && ! SkipGeneral( TheScanner, bEnd)) { LOG_ERROR( GetEMkLogger(), " Error Importing Machinings on General") ; return false ; } // ciclo di lettura delle lavorazioni while ( ! bEnd) { PtrOwner pMch ; if ( ! LoadOneMachining( TheScanner, pMch, bEnd)) { LOG_ERROR( GetEMkLogger(), ( " Error Importing Machinings : reading at line" + ToString( TheScanner.GetCurrLineNbr())).c_str()) ; continue ; } // se non c'è lavorazione (si è alla fine), si prosegue if ( IsNull( pMch)) continue ; // se la lavorazione non deve essere importata proseguo con la successiva auto it = find( vsMachiningsToImport.begin(), vsMachiningsToImport.end(), pMch->m_sName) ; if ( it == vsMachiningsToImport.end()) continue ; // assegno il nuovo nome pMch->m_sName = vsMachiningsNames[ it - vsMachiningsToImport.begin()] ; // se il suo UUID esiste già nel DB lo modifico if ( m_umData.find( pMch->m_Uuid) != m_umData.end()) { LOG_ERROR( GetEMkLogger(), ( " Warning Importing Machinings: " + pMch->m_sName + " UUID changed").c_str()) ; CreateEgtUUID( pMch->m_Uuid) ; } // aggiungo la lavorazione if ( ! m_umData.emplace( pMch->m_Uuid, pMch).second || ! m_suData.emplace( pMch->m_sName, pMch->m_Uuid).second) { LOG_ERROR( GetEMkLogger(), ( " Error Importing Machinings : failed adding " + pMch->m_sName).c_str()) ; continue ; } vsImported.push_back( pMch->m_sName) ; Release( pMch) ; } if ( ! vsImported.empty()) m_bModified = true ; return true ; } //---------------------------------------------------------------------------- bool MachiningsMgr::SkipGeneral( Scanner& TheScanner, bool& bEnd) const { // leggo la prossima linea string sLine ; if ( ! TheScanner.GetLine( sLine)) { // fine file bEnd = true ; return true ; } // deve essere dati generali if ( sLine != MF_GENERAL) return false ; bool bOk = true ; // leggo le linee successive bEnd = true ; while ( bOk && TheScanner.GetLine( sLine)) { // se sezione successiva if ( sLine.front() == '[' && sLine.back() == ']') { TheScanner.UngetLine( sLine) ; bEnd = false ; break ; } } return bOk ; }