diff --git a/.vscode/settings.json b/.vscode/settings.json index a4b88aa..5452152 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -108,7 +108,8 @@ "EgtFrame", "EgtReplaceString", "EgtCircle", - "EgtArc2PV" + "EgtArc2PV", + "EgtTdbSetCurrTool" ], "Lua.diagnostics.disable": [ "empty-block" diff --git a/CAMAuto/LuaLibs/WinExec.lua b/CAMAuto/LuaLibs/WinExec.lua new file mode 100644 index 0000000..b975612 --- /dev/null +++ b/CAMAuto/LuaLibs/WinExec.lua @@ -0,0 +1,742 @@ +-- WinExec.lua by Egalware s.r.l. 2024/06/13 +-- Libreria esecuzione lavorazioni per Serramenti + +-- Tabella per definizione modulo +local WinExec = {} + +-- Include +require( 'EgtBase') + +-- Carico i dati globali +local WinData = require( 'WinData') + +EgtOutLog( ' WinExec started', 1) +EgtMdbSave() + +------------------------------------------------------------------------------------------------------------- +-- *** variabili globali *** +------------------------------------------------------------------------------------------------------------- +TOOLS = nil +STRATEGIES = nil +MACHININGS = nil + +------------------------------------------------------------------------------------------------------------- +-- *** COSTANTI *** TODO -> DA SPOSTARE IN BEAMDATA??? +------------------------------------------------------------------------------------------------------------- +TH_DIAMETER_HSK63 = 63 +TH_LENGTH_HSK63 = 75 +SIDEANGLE_DOVETAIL = 15 + +------------------------------------------------------------------------------------------------------------- +-- *** funzioni di base *** +------------------------------------------------------------------------------------------------------------- +local function GetToolTypeNameFromToolTypeID( dToolTypeID) + if dToolTypeID == MCH_TY.DRILL_STD then + return 'DRILL_STD', 'DRILLBIT' + elseif dToolTypeID == MCH_TY.DRILL_LONG then + return 'DRILL_LONG', 'DRILLBIT' + elseif dToolTypeID == MCH_TY.SAW_STD then + return 'SAW_STD', 'SAWBLADE' + elseif dToolTypeID == MCH_TY.SAW_FLAT then + return 'SAW_FLAT', 'SAWBLADE' + elseif dToolTypeID == MCH_TY.MILL_STD then + return 'MILL_STD', 'MILL' + elseif dToolTypeID == MCH_TY.MILL_NOTIP then + return 'MILL_NOTIP', 'MILL' + elseif dToolTypeID == MCH_TY.MORTISE_STD then + return 'MORTISE_STD', 'MORTISE' + else + return nil + end +end + +------------------------------------------------------------------------------------------------------------- +local function IsToolOk( Tool) + -- controllo che i dati necessari siano impostati + if Tool.dMaxMaterial and Tool.dDiameter and Tool.dLength and Tool.sHead and Tool.sUUID then + -- se DRILLBIT non ho altri dati + if Tool.sFamily == 'DRILLBIT' then + return true + -- altrimenti controllo dati aggiuntivi altre famiglie di utensili + elseif Tool.sFamily == 'MORTISE' then + if Tool.dCornerRadius then + return true + end + else + if Tool.dThickness then + return true + end + end + end + + return false +end + +------------------------------------------------------------------------------------------------------------- +function WinExec.GetToolsFromDB() + +-- TODO gli utensili profilati devono essere messi in una lista a parte ad accesso diretto TOOLS['Prof1'] in modo da non dover scorrere la lista +-- dato che saranno molti e l'applicazione sarà diretta. Non serve ciclarli + + + -- creo lista globale utensili disponibili + TOOLS = {} + -- lista appoggio utensile + local Tool = {} + + -- recupero tutti gli utensili : punte a forare, lame, frese e motoseghe + Tool.sName = EgtTdbGetFirstTool( MCH_TF.DRILLBIT + MCH_TF.SAWBLADE + MCH_TF.MILL + MCH_TF.MORTISE) + while Tool.sName ~= '' do + -- imposto utensile come corrente per recuperarne i dati + EgtTdbSetCurrTool( Tool.sName) + + -- verifico se utensile disponibile in attrezzaggio attuale e che abbia un tipo ben definito + local bToolLoadedOnSetup, sToolTCPos = EgtFindToolInCurrSetup( Tool.sName) + local nToolTypeId = EgtTdbGetCurrToolParam( MCH_TP.TYPE) + local sToolType, sToolFamily = GetToolTypeNameFromToolTypeID( nToolTypeId) + + -- se verifica condizioni minime, recupero tutti gli altri dati + if bToolLoadedOnSetup and sToolType then + -- TODO Tool.AName -> da rimuovere perchè non serve. Per il momento lo manteniamo solo perchè è più facile vedere e interpretare la lista utensili nella watch di zerobrane + -- Nell'utilizzo, si legge sempre il Tool.Name + Tool.AName = Tool.sName + Tool.sTcPos = sToolTCPos + Tool.sFamily = sToolFamily + Tool.sType = sToolType + Tool.nTypeId = nToolTypeId + Tool.dMaxMaterial = EgtTdbGetCurrToolParam( MCH_TP.MAXMAT) + Tool.dDiameter = EgtTdbGetCurrToolParam( MCH_TP.DIAM) + Tool.dLength = EgtTdbGetCurrToolParam( MCH_TP.LEN) + Tool.dSpeed = EgtTdbGetCurrToolParam( MCH_TP.SPEED) + Tool.bIsCCW = Tool.dSpeed < 0 + Tool.Feeds = {} + Tool.Feeds.dFeed = EgtTdbGetCurrToolParam( MCH_TP.FEED) + Tool.Feeds.dStartFeed = EgtTdbGetCurrToolParam( MCH_TP.STARTFEED) + Tool.Feeds.dEndFeed = EgtTdbGetCurrToolParam( MCH_TP.ENDFEED) + Tool.Feeds.dTipFeed = EgtTdbGetCurrToolParam( MCH_TP.TIPFEED) + -- TODO serve funzione in BeamData che data la posizione dell'utensile e della testa, capisca il montaggio (testa sopra - testa sotto - aggregato - ecc...) + Tool.sHead = EgtTdbGetCurrToolParam( MCH_TP.HEAD) + Tool.SetupInfo = {} + Tool.SetupInfo = BeamData.GetSetupInfo( Tool.sHead) + Tool.sUUID = EgtTdbGetCurrToolParam( MCH_TP.UUID) + Tool.sUserNotes = EgtTdbGetCurrToolParam( MCH_TP.USERNOTES) + Tool.dMaxDepth = EgtTdbGetCurrToolMaxDepth() or Tool.dMaxMaterial + Tool.ToolHolder = {} + Tool.ToolHolder.dDiameter = EgtTdbGetCurrToolThDiam() or TH_DIAMETER_HSK63 -- diametro standard HSK63 + Tool.ToolHolder.dLength = EgtTdbGetCurrToolThLength() or TH_LENGTH_HSK63 -- lunghezza standard HSK63 + -- parametri scritti nelle note + Tool.nDouble = EgtGetValInNotes( Tool.sUserNotes, 'DOUBLE') + + -- lettura parametri non comuni ( famiglia DRILLBIT non ha parametri specifici) + if sToolFamily ~= 'DRILLBIT' then + Tool.dThickness = EgtTdbGetCurrToolParam( MCH_TP.THICK) + Tool.dLongitudinalOffset = EgtTdbGetCurrToolParam( MCH_TP.LONOFFSET) + Tool.dRadialOffset = EgtTdbGetCurrToolParam( MCH_TP.RADOFFSET) + -- recupero parametri propri delle frese + if sToolFamily == 'MILL' then + Tool.dStemDiameter = EgtTdbGetCurrToolParam( MCH_TP.STEMDIAM) or Tool.ToolHolder.dDiameter -- se non settato, considero diametro come ToolHolder + Tool.dSideAngle = EgtTdbGetCurrToolParam( MCH_TP.SIDEANG) or 0 + -- verifico che parametri siano compatibili con una fresa a coda di rondine ( angolo di fianco standard Coda di rondine -> 15°) + Tool.bIsDoveTail = Tool.Type == 'MILL_NOTIP' and abs( abs( Tool.dSideAngle) - SIDEANGLE_DOVETAIL) < 1 + -- verifico che sia una fresa tipo T-Mill o BlockHaus + Tool.dSideDepth = EgtGetValInNotes( Tool.sUserNotes, 'SIDEDEPTH') or 0 -- se non settato nell'utensile, dico che non ha massimo affondamento laterale + Tool.bIsTMill = Tool.dSideDepth > 0 + Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP') or ( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero metà del tagliente + Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP') or floor( Tool.dDiameter / 3) -- se non settato nell'utensile, considero metà del diametro + Tool.bIsPen = abs( Tool.dSpeed) < 5 + -- recupero parametri propri delle lame + elseif sToolFamily == 'SAWBLADE' then + Tool.bIsUsedForLongCut = EgtGetValInNotes( Tool.sUserNotes, 'LONGCUT') == 1 or false -- false coem valore di default + Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP') or Tool.dThickness -- se non settato nell'utensile, considero lo spessore lama + Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP') or Tool.dMaxMaterial -- se non settato nell'utensile, considero un quarto del diametro + -- recupero parametri propri delle motoseghe + elseif sToolFamily == 'MORTISE' then + Tool.dDistance = EgtTdbGetCurrToolParam( MCH_TP.DIST) or 90 -- 90mm dimensione standard aggregato catena + Tool.bIsMortise = EgtGetValInNotes( Tool.sUserNotes, 'MORTISE') == 1 + Tool.bIsChainSaw = not Tool.bIsMortise + Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP') or floor( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero un terzo della lunghezza + Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP') or ( Tool.dThickness - 1) -- se non settato nell'utensile, considero spessore catena meno 1mm di sicurezza + Tool.dCornerRadius = EgtTdbGetCurrToolParam( MCH_TP.CORNRAD) + Tool.dWidth = Tool.dDiameter + end + end + + -- se tutti i dati necessari sono disponibili, inserisco utensile nella lista globale degli utensili disponibili + if IsToolOk( Tool) then + table.insert( TOOLS, Tool) + -- altrimenti scrivo nel log che l'utensile non è conforme + else + EgtOutLog( '*** ' .. Tool.sName .. ' : NOT-COMPLIANT ***', 1) + end + + -- reset dati + Tool = {} + end + + -- recupero utensile successivo ( punte a forare, lame, frese e motoseghe) + Tool.sName = EgtTdbGetNextTool( MCH_TF.DRILLBIT + MCH_TF.SAWBLADE + MCH_TF.MILL + MCH_TF.MORTISE) + end +end + + +------------------------------------------------------------------------------------------------------------- +-- *** Inserimento delle lavorazioni nelle travi *** +------------------------------------------------------------------------------------------------------------- + +local function GetStrategiesFromGlobalList( Proc) + -- cerco tra le feature + for i = 1, #STRATEGIES.Features do + -- se trovo la feature + if Proc.nPrc == STRATEGIES.Features[i].nPrc and Proc.nGrp == STRATEGIES.Features[i].nGrp then + -- cerco tra le topologie + for j = 1, #STRATEGIES.Features[i].Topologies do + -- se trovo la topologia + if Proc.Topology.sName == STRATEGIES.Features[i].Topologies[j].sName then + -- ritorno le strategie disponibili per la feature che sto analizzando + return STRATEGIES.Features[i].Topologies[j].Strategies + end + end + end + end + return nil +end + +------------------------------------------------------------------------------------------------------------- +local function GetStrategies( Proc) + local AvailableStrategiesForProc = nil + -- se la lista STRATEGIES è stata letta da JSON (quindi non è vuota), ritorno le strategie possibili + if STRATEGIES and #STRATEGIES.Features > 0 then + AvailableStrategiesForProc = GetStrategiesFromGlobalList( Proc) + end + -- se non ho trovato strategie disponibili nel JSON, o se JSON non presente, lancio script che setta le strategie in modo statico, come definito con cliente + if not AvailableStrategiesForProc then + AvailableStrategiesForProc = BCS.GetStrategiesFromBasicCustomerStrategies( Proc) + end + return AvailableStrategiesForProc +end + +------------------------------------------------------------------------------------------------------------- +local function GetFeatureForcedStrategy( Proc) + -- cerco nelle note se è stata forzata una strategia specifica + local sStrategyId = EgtGetInfo( Proc.id, 'STRATEGY', 's') + + -- se è presente la strategia forzata + if sStrategyId then + -- eseguo file config con i parametri di default + local StrategyData = require( sStrategyId .. '\\' .. sStrategyId .. 'Config') + + -- se ID strategia non esiste oppure ID letto in NGE è differente da quello letto nella strategia, esco subito + if not StrategyData or StrategyData.sStrategyId ~= sStrategyId then + return nil + end + + -- salvo che questa strategia è stata forzata, e che quindi ho già letto il file config con i parametri di default + -- quando si calcolerà la lavorazione non servirà leggere/riverificare i parametri di default + StrategyData.bForcedStrategy = true + + -- cerco e aggiorno i parametri come sono settati nel processing + for i = 1, #StrategyData.Parameters do + local sParameterToRead = StrategyData.sStrategyId .. '_' .. StrategyData.Parameters[i].sName + ForcedParameterForProc = EgtGetInfo( Proc.id, sParameterToRead, 's') + -- se ho trovato il valore, lo sovrascrivo al default + if ForcedParameterForProc then + StrategyData.Parameters[i].sValue = ForcedParameterForProc + end + end + + -- ritorno la lista strategia con parametri + local StrategyToProc = {} + table.insert( StrategyToProc, StrategyData) + return StrategyToProc + end + return nil +end + +------------------------------------------------------------------------------------------------------------- +local function CollectFeatures( Part) + -- recupero le feature + local vProc = {} + local LayerId = {} + LayerId[1] = BeamLib.GetAddGroup( Part.id) + LayerId[2] = EgtGetFirstNameInGroup( Part.id or GDB_ID.NULL, 'Processings') + for nInd = 1, 2 do + local ProcId = EgtGetFirstInGroup( LayerId[nInd] or GDB_ID.NULL) + while ProcId do + local nEntType = EgtGetType( ProcId) + if nEntType == GDB_TY.SRF_MESH or nEntType == GDB_TY.EXT_TEXT or + nEntType == GDB_TY.CRV_LINE or nEntType == GDB_TY.CRV_ARC or nEntType == GDB_TY.CRV_BEZ or nEntType == GDB_TY.CRV_COMPO then + local nGrp = EgtGetInfo( ProcId, 'GRP', 'i') + local nPrc = EgtGetInfo( ProcId, 'PRC', 'i') + local nDo = EgtGetInfo( ProcId, 'DO', 'i') or 1 + if nGrp and nPrc and nDo == 1 then + local Proc = {} + Proc.idPart = Part.id + Proc.id = ProcId + -- id della feature btl ( se non presente info, si prende id dell'entità geometrica) + Proc.idFeature = EgtGetInfo( Proc.id, 'PRID', 's') or Proc.id + Proc.nGrp = nGrp + Proc.nPrc = nPrc + Proc.nFlg = 1 + Proc.nFct = EgtSurfTmFacetCount( ProcId) or 0 + Proc.idCut = EgtGetInfo( EgtGetParent( EgtGetParent( ProcId)), 'CUTID', 'i') or 0 + Proc.idTask = EgtGetInfo( ProcId, 'TASKID', 'i') or 0 + Proc.b3Box = EgtGetBBoxGlob( ProcId, GDB_BB.STANDARD) + EgtOutLog( '------Feature ' .. Proc.idFeature .. '------') + -- se esiste la geometria + if Proc.b3Box and not Proc.b3Box:isEmpty() then + -- TODO fare una funzione per recuperare i dati delle feature che non passano dal calcolo della topologia? + -- se foro calcolo altri dati + if ID.IsDrilling( Proc) then + -- assegno diametro e facce di ingresso e uscita (dati tabelle sempre per riferimento) + Proc.dDiam, Proc.dLen, Proc.nFcs, Proc.nFce = FeatureData.GetDrillingData( Proc) + end + -- informazioni facce e topologia + Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part) + -- calcolo topologia solo se necessario, altrimenti si sfruttano le informazioni della feature BTL + Proc.Topology = {} + if FeatureData.NeedTopologyFeature( Proc) then + Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc) + Proc.Faces = FaceData.GetFacesInfo( Proc, Part) + Proc.Topology = FeatureData.ClassifyTopology( Proc, Part) + else + Proc.Topology.sFamily = 'FEATURE' + Proc.Topology.sName = 'FEATURE' + end + -- se topologia feature riconosciuta, oppure da non calcolare perchè il riconoscimento topologico è basato sulla feature stessa + if Proc.Topology.sName ~= 'NOT_IMPLEMENTED' then + Proc.MainFaces = FaceData.GetMainFaces( Proc, Part) + -- se la processing ha una strategia forzata, riporto tutto nella proc + local vForcedStrategy = GetFeatureForcedStrategy( Proc) + if vForcedStrategy then + Proc.AvailableStrategies = vForcedStrategy + -- altrimenti cerco tra le strategie disponibili + else + Proc.AvailableStrategies = GetStrategies( Proc) + end + -- se ci sono strategie disponibili, aggiungo a lista delle feature da lavorare + if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then + table.insert( vProc, Proc) + -- altrimenti errore (non ci sono strategie per lavorare la topologia riconosciuta) + else + EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies') + end + -- altrimenti errore (serviva riconoscimento topologico, ma non è stato possibile farlo) + else + EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies') + end + else + Proc.nFlg = 0 + table.insert( vProc, Proc) + EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' is empty (no geometry)') + end + end + end + ProcId = EgtGetNext( ProcId) + end + end + return vProc +end + +------------------------------------------------------------------------------------------------------------- +local function GetFeatureInfoAndDependency( vProcSingleRot, Part) + -- ciclo tutte le feature + for i = 1, #vProcSingleRot do + local Proc = vProcSingleRot[i] + -- controllo la feature con tutte le altre per recuperare le dipendenze + for j = 1, #vProcSingleRot do + -- non si controlla la feature con se stessa + if i ~= j then + local ProcB = vProcSingleRot[j] + -- verifico se feature tipo LapJoint è attraversata da almeno un foro + if ( Proc.Topology.sFamily == 'Pocket' or Proc.Topology.sFamily == 'Tunnel' or Proc.Topology.sFamily == 'Groove' or ID.IsMortise( Proc)) and + ID.IsDrilling( ProcB) and Overlaps( Proc.b3Box, ProcB.b3Box) then + Proc.bPassedByHole = true + end + -- verifiche per specchiature + if BeamData.DOWN_HEAD or BeamData.TWO_EQUAL_HEADS then + -- forature + if BeamData.DOUBLE_HEAD_DRILLING and ID.IsDrilling( Proc) and ID.IsDrilling( ProcB) and not Proc.Mirror then + if AreDrillingsMirrored( Proc, ProcB, Part) then + Proc.Mirror = ProcB + end + end + end + end + end + end + return vProcSingleRot +end + +------------------------------------------------------------------------------------------------------------- +local function RunStrategyLibraries( sStrategyId) + local StrategyConfigName = sStrategyId .. '\\' .. sStrategyId .. 'Config' + local StrategyConfigPath = BEAM.BASEDIR .. '\\Strategies\\' .. StrategyConfigName .. '.lua' + local StrategyScriptName = sStrategyId .. '\\' .. sStrategyId + local StrategyScriptPath = BEAM.BASEDIR .. '\\Strategies\\' .. StrategyScriptName .. '.lua' + local StrategyLib = {} + if EgtExistsFile( StrategyConfigPath) and EgtExistsFile( StrategyScriptPath) then + StrategyLib.Config = require( StrategyConfigName) + -- eseguo file script strategia + StrategyLib.Script = require( StrategyScriptName) + return StrategyLib + else + return {} + end +end + +------------------------------------------------------------------------------------------------------------- +-- ritorna l'indice della strategia migliore, tra le due passate +local function GetIndexBestStrategyFromComparison( AvailableStrategies, nIndex1, nIndex2) + local dChosenIndex = 0 + -- controllo indici + if nIndex1 == 0 and nIndex2 == 0 then + dChosenIndex = 0 + elseif nIndex1 == 0 then + -- basta che sia applicabile + if AvailableStrategies[nIndex2].Result and ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' or AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed') then + dChosenIndex = nIndex2 + end + elseif nIndex2 == 0 then + -- basta che sia applicabile + if AvailableStrategies[nIndex1].Result and ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' or AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed') then + dChosenIndex = nIndex1 + end + elseif not AvailableStrategies[nIndex1].Result or not AvailableStrategies[nIndex2].Result then + dChosenIndex = 0 + elseif ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Completed') or + ( AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex2].Result.sStatus == 'Not-Applicable') then + dChosenIndex = nIndex1 + elseif ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' and AvailableStrategies[nIndex1].Result.sStatus ~= 'Completed') or + ( AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex1].Result.sStatus == 'Not-Applicable') then + dChosenIndex = nIndex2 + else + -- se le due strategie hanno stesso stato e sono entrambe applicabili (quindi entrambe complete o entrambe non-complete) + if AvailableStrategies[nIndex1].Result.sStatus ~= 'Not-Applicable' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Not-Applicable' and + AvailableStrategies[nIndex1].Result.sStatus == AvailableStrategies[nIndex2].Result.sStatus then + local dCompositeRatingStrategy1 = AvailableStrategies[nIndex1].Result.nQuality * AvailableStrategies[nIndex1].Result.nCompletionIndex * AvailableStrategies[nIndex1].Result.dMRR + local dCompositeRatingStrategy2 = AvailableStrategies[nIndex2].Result.nQuality * AvailableStrategies[nIndex2].Result.nCompletionIndex * AvailableStrategies[nIndex2].Result.dMRR + -- si predilige strategia con rating composito più alto + if dCompositeRatingStrategy1 > dCompositeRatingStrategy2 then + dChosenIndex = nIndex1 + elseif dCompositeRatingStrategy2 > dCompositeRatingStrategy1 then + dChosenIndex = nIndex2 + -- altrimenti si prende la strategia con indice più basso + else + dChosenIndex = EgtIf( nIndex1 < nIndex2, nIndex1, nIndex2) + end + end + end + return dChosenIndex +end + +------------------------------------------------------------------------------------------------------------- +-- funzione che processa tutte le feature sul pezzo e trova la miglior strategia di lavorazione tra quelle disponibili +local function GetBestStrategy( vProcSingleRot, Part) + -- per ogni feature + for i = 1, #vProcSingleRot do + -- processo tutte le feature attive + local Proc = vProcSingleRot[i] + if Proc.nFlg ~= 0 then + -- controllo se ci sono strategie disponibili + if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then + local nIndexBestStrategy = 0 + -- ciclo tutte le strategie della feature + for nIndexCurrentStrategy = 1, #Proc.AvailableStrategies do + -- eseguo file config con i parametri di default + local CurrentStrategy = {} + CurrentStrategy = RunStrategyLibraries( Proc.AvailableStrategies[nIndexCurrentStrategy].sStrategyId) + -- controllo che le librerie siano state effettivamente caricate + if CurrentStrategy.Config and CurrentStrategy.Script then + local bStrategyOk + -- eseguo la strategia solo come calcolo fattibilità e voto. Non si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati + _, Proc.AvailableStrategies[nIndexCurrentStrategy].Result = CurrentStrategy.Script.Make( false, Proc, Part, Proc.AvailableStrategies[nIndexCurrentStrategy].Parameters) + -- scelgo la migliore strategia tra le due + nIndexBestStrategy = GetIndexBestStrategyFromComparison( Proc.AvailableStrategies, nIndexCurrentStrategy, nIndexBestStrategy) + + -- se scelta strategia standard, esco subito alla prima che trovo completa + -- TODO serve paraemtro da Beam&Wall ( oppure da confirgurazione) !!!!!!!! + if BEAM.GetFirstCompletedStrategy and nIndexBestStrategy > 0 then + if Proc.AvailableStrategies[nIndexBestStrategy].Result.sStatus == 'Complete' then + break + end + end + + -- se non trovo i file della strategia, scrivo che non è più disponibile + else + Proc.AvailableStrategies[nIndexCurrentStrategy].Result = {} + Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sInfo = 'Strategy not found' + end + end + -- salvo sulla proc la migliore strategia + if nIndexBestStrategy ~= 0 then + Proc.ChosenStrategy = Proc.AvailableStrategies[nIndexBestStrategy] + end + end + end + end + return vProcSingleRot +end + +------------------------------------------------------------------------------------------------------------- +-- Ordina le feature in base a fase di lavorazione +-- 1) Head : ( intestatura) +-- 2) Standard : ( lavorazioni standard che non impattano sulal coda) +-- 3) AdvanceTail : ( lavorazioni che richiedono taglio di separazione, ma che devono essere fatte prima perchè il taglio di separazione farebbe cadere il pezzo) +-- 4) Split : ( taglio di separazione) +-- 5) Tail : ( lavorazioni di coda, fatte dopo il taglio di separazione) +local function OrderFeatures( vProc) + local vProcToSort = vProc + -- funzione di confronto. TRUE = B1 prima di B2. FALSE = B2 prima di B1 + local function CompareFeatures( B1, B2) + -- se secondo disabilitato, va lasciato dopo + if B1.nFlg ~= 0 and B2.nFlg == 0 then + return true + -- se entrambi disabilitati seguo l'Id + elseif B1.nFlg == 0 and B2.nFlg == 0 then + return ( B1.id < B2.id) + -- se in rotazioni diverse, si mette in ordine di rotazioni + elseif B1.nRot ~= B2.nRot then + return ( B1.nRot < B2.nRot) + -- se primo è taglio di testa, va prima degli altri casi + elseif B1.Head and ( B2.Standard or B2.AdvanceTail or B2.Split or B2.Tail) then + return true + -- se primo è taglio standard, va prima degli altri casi + elseif B1.Standard and ( B2.AdvanceTail or B2.Split or B2.Tail) then + return true + -- se primo è taglio di testa anticipata, va prima degli altri casi + elseif B1.AdvanceTail and ( B2.Split or B2.Tail) then + return true + -- se primo è taglio di separazione, va prima delle lavorazioni di coda + elseif B1.Split and B2.Tail then + return true + -- se da lavorare in stessa fase pezzo + elseif B1.Head == B2.Head or B1.Standard == B2.Standard or B1.AdvanceTail == B2.AdvanceTail or B1.Split == B2.Split or B1.Tail == B2.Tail then + -- confronto standard + if abs( B1.b3Box:getCenter():getX() - B2.b3Box:getCenter():getX()) > 0.4 * ( B1.b3Box:getDimX() + B2.b3Box:getDimX()) then + return B1.b3Box:getCenter():getX() > B2.b3Box:getCenter():getX() + elseif abs( B1.b3Box:getCenter():getY() - B2.b3Box:getCenter():getY()) > 0.2 * ( B1.b3Box:getDimY() + B2.b3Box:getDimY()) then + return B1.b3Box:getCenter():getY() > B2.b3Box:getCenter():getY() + elseif abs( B1.b3Box:getCenter():getZ() - B2.b3Box:getCenter():getZ()) > 0.1 * ( B1.b3Box:getDimZ() + B2.b3Box:getDimZ()) then + return B1.b3Box:getCenter():getZ() > B2.b3Box:getCenter():getZ() + end + -- altrimenti si inverte + else + return false + end + end + + -- test della funzione di ordinamento + if EgtGetDebugLevel() >= 3 then + EgtOutLog( ' CompareFeatures Test ') + local bCompTest = true + for i = 1, #vProcToSort do + for j = i + 1, #vProcToSort do + local bComp1 = CompareFeatures( vProcToSort[i], vProcToSort[j]) + local bComp2 = CompareFeatures( vProcToSort[j], vProcToSort[i]) + if bComp1 == bComp2 then + bCompTest = false + EgtOutLog( string.format( ' ProcId : %d vs %d --> ERROR', vProcToSort[i].id, vProcToSort[j].id)) + end + end + end + if bCompTest then + EgtOutLog( ' ALL OK') + end + end + + -- eseguo ordinamento + table.sort( vProcToSort, CompareFeatures) + + return vProcToSort +end + +------------------------------------------------------------------------------------------------------------- +-- esegue le strategie migliori che ha precedentemente scelto +local function CalculateMachinings( vProc, Part) + local bAreAllApplyOk = true + MACHININGS = {} + -- applico le strategie scelte + for i = 1, #vProc do + -- processo tutte le feature attive applicando le lavorazioni + local Proc = vProc[i] + if Proc.nFlg ~= 0 and Proc.ChosenStrategy then + -- carico file script strategia (non serve verificare presenza del file perchè già fatto durante scelta strategia) + local StrategyScriptName = Proc.ChosenStrategy.sStrategyId .. '\\' .. Proc.ChosenStrategy.sStrategyId + local StrategyScript = require( StrategyScriptName) + -- eseguo la strategia e si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati + bAreAllApplyOk, _ = StrategyScript.Make( true, Proc, Part, Proc.ChosenStrategy.Parameters) + end + end + return vProc +end + +------------------------------------------------------------------------------------------------------------- +local function PrintFeatures( vProc, Part) + EgtOutLog( ' RawBox=' .. tostring( Part.RawBox)) + for i = 1, #vProc do + local Proc = vProc[i] + local sOut = string.format( ' Id=%3d Grp=%1d Prc=%3d TC=%2d/%d Flg=%2d Down=%s Side=%s Head=%s Tail=%s Fcse=%1d,%1d Diam=%.2f Fct=%2d Box=%s TopoName=%s', + Proc.id, Proc.nGrp, Proc.nPrc, Proc.idTask, Proc.idCut, + Proc.nFlg, EgtIf( Proc.bDown, 'T', 'F'), EgtIf( Proc.bSide, 'T', 'F'), + EgtIf( Proc.bHead, 'T', 'F'), EgtIf( Proc.bTail, 'T', EgtIf( Proc.bAdvTail, 'A', 'F')), + Proc.nFcs, Proc.nFce, Proc.dDiam, Proc.nFct, tostring( Proc.b3Box), Proc.Topology.sName or '') + -- info speciali per Block Haus Half Lap + if Proc.nPrc == 37 then + local sSpec = string.format( ' N=%s Hd=%s', tostring( Proc.vtN or V_NULL()), EgtIf( Proc.bHeadDir, 'T', 'F')) + sOut = sOut .. sSpec + end + EgtOutLog( sOut) + end +end + +------------------------------------------------------------------------------------------------------------- +function WinExec.ProcessFeatures( PARTS) + -- ciclo sui pezzi + local nTotErr = 0 + local Stats = {} + local nOrd = 1 + local Part = {} + + for nPart = 1, #PARTS do + if not PARTS[nPart].id and PARTS[nPart].b3Raw:getDimX() < BeamData.dMinRaw then break end + + -- per ogni rotazione, calcolo come lavorare le feature per decidere posizionamento iniziale e in che rotazione verranno lavorate le singole feature + local vProcRot = {} + -- lista contenente le feature da eseguire + local vProc = {} + + +-- TODO da rimuovere o lasciare solo per debug +if EgtGetDebugLevel() >= 3 then + EgtStartCounter() +end + + + -- TODO Il numero di rotazioni da calcolare deve dipendere dalle impostazionei del cliente. Per adesso si calcolano tutte e 4, ma può essere ottimizzato + for dRotIndex = 1, 4 do + + -- recupero le feature di lavorazione della trave + table.insert( vProcRot, CollectFeatures( PARTS[nPart])) + + -- recupero informazioni ausiliarie feature e dipendenze tra feature stesse + -- TODO le dipendenze cambiano in base alla rotazione del pezzo? probabilmente no + vProcRot[dRotIndex] = GetFeatureInfoAndDependency( vProcRot[dRotIndex], PARTS[nPart]) + + -- sceglie la strategia migliore tra quelle disponibili ( presenti nella tabella vProcRot[dRotIndex].AvailableStrategies) + vProcRot[dRotIndex] = GetBestStrategy( vProcRot[dRotIndex], PARTS[nPart]) + + -- ruoto il grezzo per calcolare la fattibilità delle lavorazioni nella prossima rotazione + -- vettore movimento grezzi per rotazione di 90deg ogni step + local dDeltaYZ = PARTS[nPart].b3Raw:getDimY() - PARTS[nPart].b3Raw:getDimZ() + local vtMove = Vector3d( 0, dDeltaYZ / 2 * EgtIf( BeamData.RIGHT_LOAD, -1, 1), dDeltaYZ / 2) + local bPreMove = ( dDeltaYZ < 0) + -- ruoto le travi della fase corrente + if bPreMove then + EgtMoveRawPart( PARTS[nPart].idRaw, vtMove) + end + EgtRotateRawPart( PARTS[nPart].idRaw, X_AX(), EgtIf( BeamData.RIGHT_LOAD, -90, 90)) + if not bPreMove then + EgtMoveRawPart( PARTS[nPart].idRaw, vtMove) + end + -- aggiorno info pezzo + PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw) + PARTS[nPart].b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[nPart].id, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD) + end + + +-- TODO da rimuovere o lasciare solo per debug +if EgtGetDebugLevel() >= 3 then + local timeCollect = EgtStopCounter() + timeCollect = timeCollect + 0 + EgtOutBox( timeCollect, 'Collect calculation time') + EgtStartCounter() +end + + + -- TODO decidere come lavorare ogni feature in base alla matrice delle rotazioni + -- la matrice delle rotazioni deve già salvare sulla proc la strategia da utilizzare + -- Conviene salvarsi tutti i dati fino alla lavorazione e il ciclo di applicazione va ad applicare senza calcolare? + + -- aggiungo la fase, se non è la prima + if nOrd == 1 then + EgtSetCurrPhase( 1) + else + BeamLib.AddPhaseWithRawParts( PARTS[nPart], BeamData.ptOriXR, BeamData.dPosXR, 0) + end + local nPhase = EgtGetCurrPhase() + local nDispId = EgtGetPhaseDisposition( nPhase) + EgtSetInfo( nDispId, 'TYPE', EgtIf( PARTS[nPart].id, 'START', 'REST')) + EgtSetInfo( nDispId, 'ORD', nOrd) + EgtOutLog( ' *** Phase=' .. tostring( nPhase) .. ' Raw=' .. tostring( PARTS[nPart].idRaw) .. ' Part=' .. tostring( PARTS[nPart].id) .. ' ***', 1) + + -- debug + if EgtGetDebugLevel() >= 1 then + PrintFeatures( vProc, PARTS[nPart]) + end + EgtOutLog( ' *** AddMachinings ***', 1) + + -- TODO PROVVISORIO in attesa di scelta lavorazione in fase opportuna + vProc = vProcRot[1] + + -- ordino le features + vProc = OrderFeatures( vProc) + + -- TODO da fare + MACHININGS = {} + -- esegue le strategie migliori che ha precedentemente scelto e salva le lavorazioni nella lista globale + CalculateMachinings( vProc, PARTS[nPart]) + + -- TODO riordinare lavorazioni ottimizzando cambio utensile/spezzone ecc..., mantenendo dipendenze definite prima + -- ordino le lavorazioni + -- OrderMachining( vProc, PARTS[nPart]) + + -- aggiunge effettivamente le lavorazioni + MachiningLib.AddOperations( vProc, PARTS[nPart]) + + +-- TODO da rimuovere o lasciare solo per debug +if EgtGetDebugLevel() >= 3 then + local timeMachining = EgtStopCounter() + timeMachining = timeMachining + 0 + EgtOutBox( timeMachining, 'Machining calculation time') +end + + + EgtOutLog( ' *** End AddMachinings ***', 1) + -- passo al grezzo successivo + nOrd = nOrd + 1 + end + + -- Aggiornamento finale di tutto + EgtSetCurrPhase( 1) + local bApplOk, sApplErrors, sApplWarns = EgtApplyAllMachinings() + -- eventuale ricalcolo per macchine tipo PF (ma tengo warning del primo calcolo) + if EgtExistsInfo( EgtGetCurrMachGroup(), 'RECALC') then + EgtOutLog( ' **** RECALC ****') + bApplOk, sApplErrors, _ = EgtApplyAllMachinings() + EgtRemoveInfo( EgtGetCurrMachGroup(), 'RECALC') + end + if not bApplOk then + nTotErr = nTotErr + 1 + table.insert( Stats, {nErr = 1, sMsg=sApplErrors, nRot=0, idCut=0, idTask=0}) + elseif sApplWarns and #sApplWarns > 0 then + local vLine = EgtSplitString( sApplWarns, '\r\n') + for i = 1, #vLine do + local nPos = vLine[i]:find( '(WRN', 1, true) + if nPos then + local sData = vLine[i]:sub( nPos + 1, -2) + local vVal = EgtSplitString( sData, ',') + local nWarn = EgtGetVal( vVal[1] or '', 'WRN', 'i') + local nCutId = EgtGetVal( vVal[2] or '', 'CUTID', 'i') + if nWarn and nCutId then + table.insert( Stats, { nErr=-nWarn, sMsg=vLine[i], nRot=0, idCut=nCutId, idTask=0}) + end + end + end + end + + return ( nTotErr == 0), Stats +end + +------------------------------------------------------------------------------------------------------------- +return WinExec diff --git a/CAMAuto/ProcessWin.lua b/CAMAuto/ProcessWin.lua index 365b7af..af90998 100644 --- a/CAMAuto/ProcessWin.lua +++ b/CAMAuto/ProcessWin.lua @@ -60,6 +60,31 @@ local WinExec = require( 'WinExec') -- Variabili globali PARTS = {} +-- Colore del grezzo +local ColA = Color3d( 255, 165, 0, 30) + +--------------------------------------------------------------------- +-- Crea il grezzo che verrà messo in macchina +--------------------------------------------------------------------- +local function AlignPieces( PARTS) + for i = 1, #PARTS do + -- TODO allinea X a globale, ma bisognerebbe tenere in considerazione l'orientamento pezzi che vuole la macchina + local dRotX, dRotY, dRotZ = GetFixedAxesRotABCFromFrame( PARTS[i].idFrame) + + local ptOrigFrame = getOrigin( PARTS[i].idFrame) + + if dRotX ~= 0 then + EgtRotate( PARTS[i].id, ptOrigFrame, X_AX(), dRotX) + end + if dRotY ~= 0 then + EgtRotate( PARTS[i].id, ptOrigFrame, Y_AX(), dRotY) + end + if dRotZ ~= 0 then + EgtRotate( PARTS[i].id, ptOrigFrame, Z_AX(), dRotZ) + end + end +end + ------------------------------------------------------------------------------------------------------------- -- *** Recupero i pezzi da processare *** ------------------------------------------------------------------------------------------------------------- @@ -86,8 +111,17 @@ local function MyProcessInputData() EgtOutBox( 'Non sono state selezionati pezzi', 'Lavora Pezzi', 'ERROR') return false else + -- allineo i pezzi per come se li aspetta la macchina + AlignPieces() + -- recupero tutte le dimensioni necessarie local sOut = '' for i = 1, #PARTS do + PARTS[i].idSolid = EgtGetFirstNameInGroup( PARTS[i].id, 'Solid') + PARTS[i].idFrame = EgtGetFirstNameInGroup( EgtGetFirstNameInGroup( PARTS[i].id, 'Geo'), 'Frame') + PARTS[i].b3Solid = EgtGetBBoxGlob( PARTS[i].idSolid or GDB_ID.NULL, GDB_BB.STANDARD) + PARTS[i].dLength = PARTS[1].b3Solid:getDimX() + PARTS[i].dWidth = PARTS[1].b3Solid:getDimY() + PARTS[i].dHeight = PARTS[1].b3Solid:getDimZ() sOut = sOut .. PARTS[i].sName .. ', ' end sOut = sOut:sub( 1, -3) @@ -115,10 +149,12 @@ local function GetDispOffsetFromNotes( nPieceIndex) DispOffset.Phase1 = {} DispOffset.Phase2 = {} - DispOffset.Phase1.dOffsetY = EgtGetInfo( PIECES[nPieceIndex].id, 'OFFY_1', 'd') - DispOffset.Phase1.dOffsetZ = EgtGetInfo( PIECES[nPieceIndex].id, 'OFFZ_1', 'd') - DispOffset.Phase2.dOffsetY = EgtGetInfo( PIECES[nPieceIndex].id, 'OFFY_2', 'd') - DispOffset.Phase2.dOffsetZ = EgtGetInfo( PIECES[nPieceIndex].id, 'OFFZ_2', 'd') + DispOffset.Phase1.dOffsetX = 0 -- dovrà essere calcolato in base alle lavorazioni + DispOffset.Phase1.dOffsetY = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFY_1', 'd') + DispOffset.Phase1.dOffsetZ = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFZ_1', 'd') + DispOffset.Phase2.dOffsetX = 0 -- dovrà essere calcolato in base alle lavorazioni + DispOffset.Phase2.dOffsetY = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFY_2', 'd') + DispOffset.Phase2.dOffsetZ = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFZ_2', 'd') -- controllo se tutti gli offset sono settati if DispOffset.Phase1.dOffsetY and DispOffset.Phase1.dOffsetZ and DispOffset.Phase2.dOffsetY and DispOffset.Phase2.dOffsetZ then @@ -131,13 +167,13 @@ end ------------------------------------------------------------------------------------------------------------- local function GetDispOffsetFromInput( nPieceIndex) -- assegno alle stringhe i valori letti, in modo che vengano proposti quelli nel dialogo - local sOffYPh1 = EgtIf( PIECES[nPieceIndex].DispOffset.Phase1.dOffsetY, tostring( PIECES[nPieceIndex].DispOffset.Phase1.dOffsetY), tostring( PIECES[nPieceIndex].dWidth / 2)) - local sOffZPh1 = EgtIf( PIECES[nPieceIndex].DispOffset.Phase1.dOffsetZ, tostring( PIECES[nPieceIndex].DispOffset.Phase1.dOffsetZ), '0') - local sOffYPh2 = EgtIf( PIECES[nPieceIndex].DispOffset.Phase2.dOffsetY, tostring( PIECES[nPieceIndex].DispOffset.Phase2.dOffsetY), tostring( PIECES[nPieceIndex].dWidth / 2)) - local sOffZPh2 = EgtIf( PIECES[nPieceIndex].DispOffset.Phase2.dOffsetZ, tostring( PIECES[nPieceIndex].DispOffset.Phase2.dOffsetZ), '0') + local sOffYPh1 = EgtIf( PARTS[nPieceIndex].DispOffset.Phase1.dOffsetY, tostring( PARTS[nPieceIndex].DispOffset.Phase1.dOffsetY), tostring( PARTS[nPieceIndex].dWidth / 2)) + local sOffZPh1 = EgtIf( PARTS[nPieceIndex].DispOffset.Phase1.dOffsetZ, tostring( PARTS[nPieceIndex].DispOffset.Phase1.dOffsetZ), '0') + local sOffYPh2 = EgtIf( PARTS[nPieceIndex].DispOffset.Phase2.dOffsetY, tostring( PARTS[nPieceIndex].DispOffset.Phase2.dOffsetY), tostring( PARTS[nPieceIndex].dWidth / 2)) + local sOffZPh2 = EgtIf( PARTS[nPieceIndex].DispOffset.Phase2.dOffsetZ, tostring( PARTS[nPieceIndex].DispOffset.Phase2.dOffsetZ), '0') - local vInp = EgtDialogBox( 'Dati di disposizione pezzo: ' .. PIECES[nPieceIndex].sName, + local vInp = EgtDialogBox( 'Dati di disposizione pezzo: ' .. PARTS[nPieceIndex].sName, {'Sporgenza laterale FASE1', sOffYPh1}, {'Posizione Z FASE1', sOffZPh1}, {'Sporgenza laterale FASE2', sOffYPh2}, {'Posizione Z FASE2', sOffZPh2}) if not vInp or #vInp == 0 then @@ -145,10 +181,10 @@ local function GetDispOffsetFromInput( nPieceIndex) end -- salvo input nei valori che utilizzerò dopo - PIECES[nPieceIndex].DispOffset.Phase1.dOffsetY = tonumber( vInp[1]) - PIECES[nPieceIndex].DispOffset.Phase1.dOffsetZ = tonumber( vInp[2]) - PIECES[nPieceIndex].DispOffset.Phase2.dOffsetY = tonumber( vInp[3]) - PIECES[nPieceIndex].DispOffset.Phase2.dOffsetZ = tonumber( vInp[4]) + PARTS[nPieceIndex].DispOffset.Phase1.dOffsetY = tonumber( vInp[1]) + PARTS[nPieceIndex].DispOffset.Phase1.dOffsetZ = tonumber( vInp[2]) + PARTS[nPieceIndex].DispOffset.Phase2.dOffsetY = tonumber( vInp[3]) + PARTS[nPieceIndex].DispOffset.Phase2.dOffsetZ = tonumber( vInp[4]) return true end @@ -156,23 +192,48 @@ end --------------------------------------------------------------------- -- Crea il grezzo che verrà messo in macchina --------------------------------------------------------------------- -local function CreateRawPart( PIECES) - +local function AddOverMaterialToRaw( PARTS) + for i = 1, #PARTS do + PARTS[i].RawOffset = {} + -- recupero le info + PARTS[i].RawOffset.dOverMatIn = EgtGetInfo( PARTS[i].id, 'OVERMAT_IN', 'd') or 5 + PARTS[i].RawOffset.dOverMatOut = EgtGetInfo( PARTS[i].id, 'OVERMAT_OUT', 'd') or 5 + PARTS[i].RawOffset.dOverMatLeft = EgtGetInfo( PARTS[i].id, 'OVERMAT_LEFT', 'd') or 5 + PARTS[i].RawOffset.dOverMatRight = EgtGetInfo( PARTS[i].id, 'OVERMAT_RIGHT', 'd') or 5 + -- aggiungo sovramateriale + local dLength = PARTS[i].dLength + PARTS[i].RawOffset.dOverMatIn + PARTS[i].RawOffset.dOverMatOut + local dWidth = PARTS[i].dWidth + PARTS[i].RawOffset.dOverMatLeft + PARTS[i].RawOffset.dOverMatRight + local dHeight = PARTS[i].dHeight -- altezza non ha sovramteriale + EgtModifyRawPartSize( PARTS[i].idRaw, dLength, dWidth, dHeight) + end end +--------------------------------------------------------------------- +-- Crea il grezzo che verrà messo in macchina +--------------------------------------------------------------------- +local function CreateRaws( PARTS) + for i = 1, #PARTS do + PARTS[i].idRaw = EgtAddRawPartWithPart( PARTS[i].id, PARTS[i].idSolid, 0, ColA) + end +end + + ------------------------------------------------------------------------------------------------------------- -- *** Inserimento delle travi nel grezzo *** ------------------------------------------------------------------------------------------------------------- local function MyProcessPieces() -- verifico che i pezzi selezionati siano compatibili con la macchina - WinData.VerifyPieces( PIECES) + WinData.VerifyPieces( PARTS) -- si crea il grezzo - CreateRawPart( PIECES) + CreateRaws( PARTS) + + -- aggiunta sovramateriale + AddOverMaterialToRaw( PARTS) -- recupero offset per posizionamento - for i = 1, #PIECES do + for i = 1, #PARTS do local bInsertedAllOffs = GetDispOffsetFromNotes( i) -- se non sono settati nelle note, li chiedo if not bInsertedAllOffs then @@ -185,7 +246,7 @@ local function MyProcessPieces() end -- si dispongono i pezzi sulla tavola - WinData.DisposePiecesOnTable( PIECES) + WinData.DisposePiecesOnTable( PARTS) return true end @@ -194,7 +255,7 @@ end -- *** Inserimento delle lavorazioni *** ------------------------------------------------------------------------------------------------------------- local function MyProcessFeatures() - local bOk, Stats = WinExec.ProcessFeatures( PARTS) + --local bOk = WinExec.ProcessFeatures( PARTS) return true end