Files
egwwindowlua/CAMAuto/CreateMachGroups.lua
2026-02-06 10:49:06 +01:00

375 lines
15 KiB
Lua

-- Process.lua by Egalware s.r.l. 2024/06/13
-- Gestione calcolo disposizione e lavorazioni per serramenti
-- Si opera sulla macchina corrente
-- 2024/06/13 PRIMA VERSIONE
-- Intestazioni
require( 'EgtBase')
_ENV = EgtProtectGlobal()
EgtEnableDebug( false)
-- Verifico che la macchina corrente sia abilitata per la lavorazione delle Travi
local sMachDir = EgtGetCurrMachineDir()
if not sMachDir then
EgtOutBox( 'Errore nel caricamento della macchina corrente', 'Lavora Serramenti', 'ERROR')
return
end
if not EgtExistsFile( sMachDir .. '\\Window\\WinData.lua') then
EgtOutBox( 'La macchina corrente non è configurata per lavorare serramenti', 'Lavora Serramenti', 'ERROR')
return
end
-- Elimino direttori altre macchine e imposto direttorio macchina corrente per ricerca librerie
EgtRemoveBaseMachineDirFromPackagePath()
EgtAddToPackagePath( sMachDir .. '\\Window\\?.lua')
-- Segnalazione avvio
EgtOutLog( '*** Window Process Start ***', 1)
-- Carico le librerie
-- Carico i dati globali
local WinData = require( 'WinData')
-- Variabili globali
PARTS = {}
GROUPS = {}
-- Colore del grezzo
local ColA = Color3d( 255, 165, 0, 30)
-- TODO DA RIMUOVERE, il parametro deve essere passato
local WIN = {}
WIN.OPT_TYPE = 'PIECE'
-------------------------------------------------------------------------------------------------------------
-- *** Recupero offset applicato al profilo ***
-------------------------------------------------------------------------------------------------------------
local function GetDispOffsetFromNotes( nPieceIndex)
local bAllOffsetsAreOk = false
PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetX = 0 -- dovrà essere calcolato in base alle lavorazioni
PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetY = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFY_1', 'd')
PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetZ = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFZ_1', 'd')
PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetX = 0 -- dovrà essere calcolato in base alle lavorazioni
PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetY = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFY_2', 'd')
PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetZ = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFZ_2', 'd')
-- controllo se tutti gli offset siano settati
if PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetY and PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetZ and
PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetY and PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetZ then
bAllOffsetsAreOk = true
end
return bAllOffsetsAreOk
end
-------------------------------------------------------------------------------------------------------------
-- *** Recupero i pezzi da processare ***
-------------------------------------------------------------------------------------------------------------
local function GetInfoPieces()
-- Recupero le travi selezionate
local nId = EgtGetFirstPart()
while nId do
-- TODO caricare il file di costanti
-- si processano solo BOTTOMRAIL (WIN_PART_TYPES.BOTTOMRAIL = 2) o STANDARD (WIN_PART_TYPES.STD = 3)
if EgtGetInfo( nId, 'PART_TYPE', 'd') == 2 or EgtGetInfo( nId, 'PART_TYPE', 'd') == 3 then
table.insert( PARTS, { id = nId, sName = ( EgtGetName( nId) or ( 'Id=' .. tonumber( nId)))})
end
nId = EgtGetNextPart( nId)
end
if #PARTS == 0 then
EgtOutBox( 'Non ci sono pezzi', 'Lavora Pezzi', 'ERROR')
return false
else
-- recupero tutte le dimensioni necessarie
local sOut = ''
for i = 1, #PARTS do
PARTS[i].b3Raw = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[i].id, 'GeoRaw') or GDB_ID.NULL, GDB_BB.STANDARD)
local idFrame = EgtGetFirstNameInGroup( EgtGetFirstNameInGroup( PARTS[i].id, 'GeoRaw'), 'AuxFrame')
PARTS[i].frame = EgtFR( idFrame)
-- recupero offset per posizionamento
PARTS[i].DispOffsets = {}
PARTS[i].DispOffsets.Phase1 = {}
PARTS[i].DispOffsets.Phase2 = {}
local bInsertedAllOffs = GetDispOffsetFromNotes( i)
-- recupero info profili
PARTS[i].SemiProfiles = {}
table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_IN', 's') or '')
table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_OUT', 's') or '')
table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_LEFT', 's') or '')
table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_RIGHT', 's') or '')
-- TODO info deve essere passata da programma
-- recupero info finestra
PARTS[i].sWindow = EgtGetInfo( PARTS[i].id, 'WIN_ID', 's') or ''
-- TODO è lo stesso del PART_TYPE o serve altro??? da verificare!
-- tipo di pezzo
PARTS[i].sPieceType = EgtGetInfo( PARTS[i].id, 'PART_TYPE', 'd') or ''
-- definisco il pezzo per famiglia ( TODO da recuperare dal nome del pezzo, o da una nota)
-- se non sono stati inseriti o c'è stato un errore esco subito
if not bInsertedAllOffs then
return false
end
sOut = sOut .. PARTS[i].sName .. ', '
end
sOut = sOut:sub( 1, -3)
EgtOutLog( 'Pezzi : ' .. sOut, 1)
end
EgtDeselectAll()
return true
end
---------------------------------------------------------------------
-- Aggiunge al grezzo i sovramateriali pre-impostati
---------------------------------------------------------------------
local function AddOverMaterialToRaw( PiecesInMachGroup)
for i = 1, #PiecesInMachGroup do
-- prima di aggiungere sovramateriale al grezzo, calcolo dimensioni del finito
local b3Part = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PiecesInMachGroup[i].id, 'Geo') or GDB_ID.NULL, GDB_BB.STANDARD)
PiecesInMachGroup[i].b3Part = b3Part
PiecesInMachGroup[i].dPartLength = PiecesInMachGroup[i].b3Part:getDimX()
PiecesInMachGroup[i].dPartWidth = PiecesInMachGroup[i].b3Part:getDimY()
PiecesInMachGroup[i].dPartHeight = PiecesInMachGroup[i].b3Part:getDimZ()
-- recupero sovramateriale
PiecesInMachGroup[i].RawOffset = {}
-- recupero info sovramateriale
PiecesInMachGroup[i].RawOffset.dOverMatIn = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_IN', 'd') or 5
PiecesInMachGroup[i].RawOffset.dOverMatOut = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_OUT', 'd') or 5
PiecesInMachGroup[i].RawOffset.dOverMatLeft = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_LEFT', 'd') or 5
PiecesInMachGroup[i].RawOffset.dOverMatRight = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_RIGHT', 'd') or 5
PiecesInMachGroup[i].dRawLength = PiecesInMachGroup[i].RawOffset.dOverMatLeft + PiecesInMachGroup[i].dPartLength + PiecesInMachGroup[i].RawOffset.dOverMatRight
PiecesInMachGroup[i].dRawWidth = PiecesInMachGroup[i].RawOffset.dOverMatOut + PiecesInMachGroup[i].dPartWidth + PiecesInMachGroup[i].RawOffset.dOverMatIn
PiecesInMachGroup[i].dRawHeight = PiecesInMachGroup[i].dPartHeight
EgtModifyRawPartSize( PiecesInMachGroup[i].idRaw, PiecesInMachGroup[i].dRawLength, PiecesInMachGroup[i].dRawWidth, PiecesInMachGroup[i].dPartHeight)
local vtMove = Vector3d( PiecesInMachGroup[i].RawOffset.dOverMatLeft, PiecesInMachGroup[i].RawOffset.dOverMatOut, 0)
EgtMovePartInRawPart( PiecesInMachGroup[i].id, vtMove)
-- TODO da controllare, se ruotato di 180° bisognerebbe prendere dOverMatIn. Lo stesso per la X
PiecesInMachGroup[i].OffsetPartToRaw = {}
PiecesInMachGroup[i].OffsetPartToRaw.X = PiecesInMachGroup[i].RawOffset.dOverMatLeft
PiecesInMachGroup[i].OffsetPartToRaw.Y = PiecesInMachGroup[i].RawOffset.dOverMatOut
end
end
---------------------------------------------------------------------
-- Crea il grezzo che verrà messo in macchina
---------------------------------------------------------------------
local function CreateRaws( PiecesInMachGroup)
for i = 1, #PiecesInMachGroup do
-- si crea un grezzo "finto" (un cubo da 100mm) nell'origine del pezzo, verrà poi dimensionato uan volta adeguato con i vari sovramateriali
PiecesInMachGroup[i].idRaw = EgtAddRawPart( PiecesInMachGroup[i].frame:getOrigin(), 100, 100, 100, ColA)
EgtAddPartToRawPart( PiecesInMachGroup[i].id, ORIG(), PiecesInMachGroup[i].idRaw)
end
end
---------------------------------------------------------------------
-- Allinea i pezzi in base a come devono essere disposti sulla tavola
---------------------------------------------------------------------
-- TODO in questa fase bisogna già sapere qual è la prima fase e posizionare il pezzo di conseguenza
-- TODO (bassa priorità) adesso allinea sempre in X, ma bisognerebbe farlo in base ad un parametro in WINDATA che dice come si dispongono in macchina
local function AlignRawsToTable( PiecesInMachGroup)
for i = 1, #PiecesInMachGroup do
-- allineo il pezzo all'interno del grezzo
local dRotX, dRotY, dRotZ = GetFixedAxesRotABCFromFrame( PiecesInMachGroup[i].frame)
-- se devo ruotare
if abs( dRotZ) > GEO.EPS_ANG_SMALL then
EgtRotatePartInRawPart( PiecesInMachGroup[i].id, Z_AX(), -dRotZ)
end
-- sposto punto in basso a sinistra del pezzo sul punto in basso a sinistra del grezzo
local b3Part = EgtGetBBoxGlob( PiecesInMachGroup[i].id, GDB_BB.ONLY_VISIBLE)
local dPartPosX = b3Part:getMin():getX()
local dPartPosY = b3Part:getMin():getY()
local dPartPosZ = b3Part:getMin():getZ()
local b3Raw = EgtGetRawPartBBox( PiecesInMachGroup[i].idRaw)
local dRawPosX = b3Raw:getMin():getX()
local dRawPosY = b3Raw:getMin():getY()
local dRawPosZ = b3Raw:getMin():getZ()
local vtMove = Vector3d( dRawPosX - dPartPosX, dRawPosY - dPartPosY, dRawPosZ - dPartPosZ)
EgtMovePartInRawPart( PiecesInMachGroup[i].id, vtMove)
end
return true
end
-------------------------------------------------------------------------------------------------------------
-- *** Funzione per trovare nome MachGroup ***
-------------------------------------------------------------------------------------------------------------
local function NewMachGroupName()
local nMachGroupId = EgtGetFirstMachGroup()
if not nMachGroupId then return '1' end
local nMaxMachGroup = 0
while nMachGroupId do
local sMachGroupName = EgtGetMachGroupName( nMachGroupId)
local nMachGroupName = tonumber( sMachGroupName)
if nMachGroupName > nMaxMachGroup then
nMaxMachGroup = nMachGroupName
end
nMachGroupId = EgtGetNextMachGroup( nMachGroupId)
end
return tostring( nMaxMachGroup + 1)
end
-------------------------------------------------------------------------------------------------------------
-- *** Funzione per raggruppamento pezzi ***
-------------------------------------------------------------------------------------------------------------
-- TODO punteggio da verificare!!
-- Calcolo un punteggio di similitudine tra due pezzi
local function CalculateSimilarity( Part1, Part2)
-- peso lati: IN, OUT, LEFT, RIGHT (i lati lunghi hanno più peso rispetto alle teste)
local ProfilesWeights = { 3, 3, 1, 1}
local dScore = 0
for i = 1, 4 do
if Part1.SemiProfiles[i] == Part2.SemiProfiles[i] then
dScore = dScore + ProfilesWeights[i]
end
end
-- punteggio "bonus" se il tipo di pezzo è lo stesso
if Part1.sPieceType and Part2.sPieceType and Part1.sPieceType == Part2.sPieceType then
dScore = dScore + 2
end
return dScore
end
-------------------------------------------------------------------------------------------------------------
-- raggruppamento con vincoli ( lunghezza / numero pezzi)
local function GroupParts( PartsList, sOptimisationType)
-- TIPI DI OTTIMIZZAZIONE (sOptimisationType)
-- 'PIECE' : pezzo singolo
-- 'WINDOW' : ottimizza finestra
-- 'FULL_LIST': ottimizza progetto
local Groups = {}
local Used = {}
for i = 1, #PartsList do
if not Used[i] then
local Group = {}
-- Add the main part
table.insert( Group, PartsList[i])
Used[i] = true
-- se non è ottimizzazione pezzo singolo, cerco altri pezzi che possono essere lavorati nella stessa macchinata
if sOptimisationType ~= 'PIECE' then
-- cerca un altro pezzo compatibile
while true do
local nBestIndex = nil
local nBestScore = -1
for j = 1, #PartsList do
if not Used[j] then
local bCanBeGrouped = not( sOptimisationType == 'WINDOW' and PartsList[i].sWindow ~= PartsList[j].sWindow)
-- se possono essere raggruppati per tipo di ottimizzazione
if bCanBeGrouped then
local VirtualList = WinLib.TableCopyDeep( Group)
table.insert( VirtualList, PartsList[j])
bCanBeGrouped = WinData.VerifyPieces( VirtualList)
-- se possono effettivamente essere messi nella stessa macchinata
if bCanBeGrouped then
-- il punteggio è sempre calcolato rispetto al primo pezzo del gruppo
local nScore = CalculateSimilarity( PartsList[i], PartsList[j])
if nScore > nBestScore then
nBestScore = nScore
nBestIndex = j
end
end
end
end
end
if not nBestIndex then
break
end
-- Add the best matching part
table.insert( Group, PartsList[nBestIndex])
Used[nBestIndex] = true
end
end
table.insert( Groups, Group)
end
end
return Groups
end
-------------------------------------------------------------------------------------------------------------
-- *** Inserimento dei pezzi nelle macchinate ***
-------------------------------------------------------------------------------------------------------------
local function CreateMachGroups()
for j = 1, #GROUPS do
-- creo macchinata
local MachGroupName = NewMachGroupName()
local nMachGroup = EgtAddMachGroup( MachGroupName)
-- si setta gruppo appena creato come corrente
EgtSetCurrMachGroup( nMachGroup)
-- si crea il grezzo
CreateRaws( GROUPS[j])
-- allineo i pezzi come orientamento richiesto dalla macchina
AlignRawsToTable( GROUPS[j])
-- aggiungo sovramateriale ai grezzi
AddOverMaterialToRaw( GROUPS[j])
-- si dispongono i pezzi sulla tavola
local bDispOk, sErr = WinData.ExecDisposition( GROUPS[j])
if not bDispOk then
if not sErr then
sErr = 'Errore non gestito in WinData.ExecDisposition'
end
return false
end
-- Impostazione dell'attrezzaggio di default
local bOk = EgtImportSetup()
if not bOk then
EgtImportSetup( 'Default')
end
end
return true
end
-------------------------------------------------------------------------------------------------------------
-- *** Esecuzione ***
-------------------------------------------------------------------------------------------------------------
if not GetInfoPieces() then return end
GROUPS = GroupParts( PARTS, WIN.OPT_TYPE)
if not GROUPS or #GROUPS == 0 then
return
end
if not CreateMachGroups() then return end