- Corretta gestione pezzi scartati in optimizer

- Migliorata gestione tabella feature in supervisor
- Gestito elemento selezionato dopo alcuni comandi che lo resettavano
- Correzioni varie stati pezzi dopo ripartenza barra (redo)
- Gestione del nome supervisor
- Miglioramento finestre block e wait
- Correzione posizionamento pezzi in nesting da btl
This commit is contained in:
Emmanuele Sassi
2022-04-04 17:59:41 +02:00
parent ba6a718119
commit c79a584ab7
15 changed files with 147 additions and 48 deletions
+4 -3
View File
@@ -58,9 +58,10 @@ Public Enum StatusMapOpType
SupervisorReleaseStop = 11
ResetPartStart = 12
ResetPartEnd = 13
ChangeProdInSupervisorRequest = 14
ChangeProdInProdRequest = 15
OpenPageInViewOptimRequest = 16
SetPartScrapped = 14
ChangeProdInSupervisorRequest = 15
ChangeProdInProdRequest = 16
OpenPageInViewOptimRequest = 17
End Enum
Public Enum DimensionType
@@ -399,7 +399,7 @@ namespace EgtBEAMWALL.DataLayer.Controllers
localDbCtx.SaveChanges();
done = true;
// aggiorno info sullo status
StatusMapController.man.UpdateAction("", ProdId, PartId, Core.StatusMapItemType.Part, Core.StatusMapOpType.MachGroupMod, "");
StatusMapController.man.UpdateAction("", ProdId, PartId, Core.StatusMapItemType.Part, newState == Core.ItemState.Scrapped ? Core.StatusMapOpType.SetPartScrapped : Core.StatusMapOpType.MachGroupMod, "");
}
catch (Exception exc)
{
@@ -112,7 +112,10 @@
</Button>
</StackPanel>
<Grid Grid.Row="1">
<Grid Grid.Row="1" x:Name="PartFeatureGrid">
<Grid.Resources>
<EgtBEAMWALL:FeatureDataGridHeightConverter x:Key="FeatureDataGridHeightConverter"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
@@ -120,7 +123,7 @@
<EgtBEAMWALL:PartInRawPartListV DataContext="{StaticResource PartInRawPartListVM}"
Tag="{Binding Tag.SelectedMachGroup, RelativeSource={RelativeSource AncestorType={x:Type EgtBEAMWALL:LeftPanelV}}}"/>
<EgtBEAMWALL:FeatureInPartInRawPartListV Grid.Row="1"
Height="80"
Height="{Binding ActualHeight, ElementName=PartFeatureGrid, Converter={StaticResource FeatureDataGridHeightConverter}}"
DataContext="{StaticResource FeatureInPartInRawPartListVM}"
Tag="{Binding Tag.SelectedMachGroup.SelPart, RelativeSource={RelativeSource AncestorType={x:Type EgtBEAMWALL:LeftPanelV}}}"
Visibility="{Binding DataContext.FeatureList_Visibility, RelativeSource={RelativeSource AncestorType={x:Type EgtBEAMWALL:LeftPanelV}}}"/>
@@ -734,6 +734,7 @@ Public Class LeftPanelVM
End If
Next
SetDonePart(SelMachGroup, SelMachGroup.SelPart)
Map.refProjectVM.SupervisorMachGroupPanelVM.SelectedMachGroup = SelMachGroup
End Sub
Public Sub SetDonePart(MachGroup As MyMachGroupVM, Part As PartVM)
@@ -963,4 +964,22 @@ Public Class OPState
m_Id = Id
End Sub
End Class
End Class
Public Class FeatureDataGridHeightConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Dim Height As Double = CDbl(value)
If Not IsNothing(Height) Then
Return Height / 2
Else
Return 200
End If
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException
End Function
End Class
@@ -214,12 +214,12 @@ Public Class MyMachGroupVM
' se progetto travi e lavorazione interrotta da reset
If Map.refSupervisorManagerVM.CurrProd.nType = BWType.BEAM AndAlso (bResetWhileCutting OrElse dtStartTime <> DateTime.MinValue) And Not Map.refLeftPanelVM.bRestart Then
' chiedo se riprendere o rifare la barra da zero
Dim bRedo As MessageBoxResult = MessageBox.Show("Riprendere lavorazione barra corrente?", "", MessageBoxButton.YesNo, MessageBoxImage.Information)
Dim bRedo As MessageBoxResult = MessageBox.Show(EgtMsg(62509), "", MessageBoxButton.YesNo, MessageBoxImage.Information)
Select Case bRedo
Case MessageBoxResult.Yes
' segno da rifare pezzi non completamente lavorati
For Each Part As PartVM In PartVMList
Part.bDO = (Part.dtEndTime = DateTime.MinValue)
Part.SetDo(Part.dtEndTime = DateTime.MinValue)
'Part.NotifyPropertyChanged(NameOf(Part.bRedo))
Next
' se ripresa, mostro lista feature e colonne Redo
@@ -389,11 +389,10 @@ Public Class MyMachGroupVM
'aggiorno lista pezzi
BeamMachGroup.MyMachGroupM.RefreshPartList()
BeamMachGroup.MyMachGroupM.RefreshGroupData()
''' aggiorno dati utilizzo barra
''BeamMachGroup.UpdateUsage()
Core.ViewPanelVM.BWSetView(VT.ISO_SW, False)
' salvo nuovo grezzo nel progetto
Map.refSupervisorManagerVM.Save()
Dim LastDonePart As PartVM
' segno pezzi non completati come scrap
For Index = 0 To m_PartVMList.Count - 1
Dim CurrPart As PartVM = m_PartVMList(Index)
@@ -410,15 +409,22 @@ Public Class MyMachGroupVM
Part.NotifyPropertyChanged(NameOf(Part.nProduction_State))
' resetto stato redo
Part.bRedo = False
' scrivo data end su Db barra
DbControllers.m_MachGroupController.UpdateEnd(nP_Prod, nP_Machgroup, DateTime.MinValue)
DbControllers.m_MachGroupController.UpdateStatus(nP_Prod, nP_Machgroup, ItemState.Produced)
' scrivo stato end
MyMachGroupM.SetProductionState(ItemState.Produced)
dtEndTime = DateTime.MinValue
NotifyPropertyChanged(NameOf(nProduction_State))
Else
LastDonePart = CurrPart
End If
Next
' imposto fine MachGroup su fine ultimo pezzo o inizio Machgroup + 1 secondo
Dim MachGroupEnd As DateTime = dtStartTime + New TimeSpan(0, 0, 1)
If Not IsNothing(LastDonePart) AndAlso LastDonePart.dtEndTime <> DateTime.MinValue Then
MachGroupEnd = LastDonePart.dtEndTime
End If
' scrivo data end su Db barra
DbControllers.m_MachGroupController.UpdateEnd(Map.refSupervisorManagerVM.CurrProd.nProdId, Id, MachGroupEnd)
DbControllers.m_MachGroupController.UpdateStatus(Map.refSupervisorManagerVM.CurrProd.nProdId, Id, ItemState.Produced)
' scrivo stato end
MyMachGroupM.SetProductionState(ItemState.Produced)
dtEndTime = MachGroupEnd
NotifyPropertyChanged(NameOf(nProduction_State))
NotifyPropertyChanged(NameOf(Produce_IsEnabled))
MachGroup = BeamMachGroup
ElseIf Map.refSupervisorManagerVM.CurrProd.nType = BWType.WALL Then
@@ -456,7 +462,7 @@ Public Class MyMachGroupVM
'WallMachGroup.UpdateUsage()
'EgtSetView(VT.TOP, False)
End If
' aggiorno dati ultilizzo grezzo
' aggiorno dati utilizzo grezzo
MachGroup.UpdateUsage()
'passo machgroup a supervisor su Db
DbControllers.m_MachGroupController.UpdateSupervisor(Map.refSupervisorManagerVM.CurrProd.nProdId, MachGroup.Id, DbControllers.m_SupervisorId)
@@ -464,6 +470,18 @@ Public Class MyMachGroupVM
MachGroup.NotifyPropertyChanged(NameOf(MachGroup.Background))
' assegno a nuova barra indice successivo a quella di partenza
MachGroup.SetProductionIndex(m_nProductionIndex + 1)
' assegno stati a pezzi
For Each Part As PartVM In MachGroup.PartVMList
DbControllers.m_PartController.UpdateStatus(Map.refSupervisorManagerVM.CurrProd.nProdId, MachGroup.Id, Part.nPartId, ItemState.Assigned)
Part.nProduction_State = ItemState.Assigned
Part.NotifyPropertyChanged(NameOf(Part.Background))
'' disabilito impostazione modificato
'Dim DisableMgr As New DisableModifiedMgr
'' blocco modifica del pezzo
'EgtDuploSetLocked(Part.nPartId)
'' ripristino precedente impostazione modificato
'DisableMgr.ReEnable()
Next
' aggiorno indice di tutte le barre in coda
For Each CurrMachGroup As MyMachGroupVM In Map.refSupervisorMachGroupPanelVM.MachGroupVMList
If Not (CurrMachGroup Is MachGroup) AndAlso CurrMachGroup.nProductionIndex >= MachGroup.nProductionIndex AndAlso CurrMachGroup.nProductionIndex < 5000 Then
@@ -251,7 +251,7 @@ Public Class SupervisorManagerVM
DbControllers.m_StatusMapController.StartProd(Map.refSupervisorManagerVM.CurrProd.nProdId, m_SupervisorId)
Map.refSupervisorManagerVM.CurrProd.SetModificationIndex(0)
' recupero elementi modificati
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(0)
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(m_SupervisorId, 0)
' carico dictionary di tutti i Machgroup
For Each MachGroup In MachGroupList
If Not Map.refProjectVM.SupervisorMachGroupPanelVM.MachGroupFullList.ContainsKey(MachGroup.ItemId) Then
@@ -23,7 +23,7 @@ Public Class ViewerOptimizerComm
End If
' recupero indice modifica del prod
Dim nModificationIndex As Integer = 0
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd()
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd(m_SupervisorId)
For Each ActiveSession In ActiveSessionList
If ActiveSession.ItemId = Map.refSupervisorManagerVM.CurrProd.nProdId Then
nModificationIndex = ActiveSession.Index
@@ -32,7 +32,7 @@ Public Class ViewerOptimizerComm
' se indice letto diverso da indice gia' presente
If nModificationIndex <> Map.refSupervisorManagerVM.CurrProd.nModificationIndex Then
' recupero elementi modificati
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(Map.refSupervisorManagerVM.CurrProd.nModificationIndex + 1)
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(m_SupervisorId, Map.refSupervisorManagerVM.CurrProd.nModificationIndex + 1)
Dim bReloadFile As Boolean = False
For Each MachGroupModification In MachGroupList
If MachGroupModification.ItemType = Core.StatusMapItemType.MachGroup Then
@@ -19,7 +19,8 @@
</Grid.RowDefinitions>
<TextBlock Grid.Row="1"
Text="Attesa salvataggio in ottimizzatore"
TextAlignment="Center"/>
TextAlignment="Center"
Margin="40,0,40,0"/>
<ProgressBar Grid.Row="3"
Minimum="0"
Maximum="20"
@@ -29,6 +30,7 @@
<Button Grid.Row="5" Content="Cancel"
IsDefault="False"
IsCancel="True"
Command="{Binding Cancel_Command}"/>
Command="{Binding Cancel_Command}"
Style="{StaticResource EgtWPFLib5_InputButton}"/>
</Grid>
</EgtWPFLib5:EgtCustomWindow>
@@ -1,6 +1,7 @@
Imports System.Windows.Threading
Imports EgtBEAMWALL.Core
Imports EgtWPFLib5
Imports EgtUILib
Public Class WaitingWndVM
Inherits VMBase
@@ -18,6 +19,23 @@ Public Class WaitingWndVM
End Get
End Property
#Region "Messages"
Public ReadOnly Property Msg_Text As String
Get
Return EgtMsg(61968)
End Get
End Property
Public ReadOnly Property Cancel_Msg As String
Get
Return EgtMsg(61969)
End Get
End Property
#End Region ' Messages
' definizione comandi
Private m_cmdCancel As ICommand
@@ -20,7 +20,8 @@
</Grid.RowDefinitions>
<TextBlock Grid.Row="1"
Text="{Binding Msg_Text}"
TextAlignment="Center"/>
TextAlignment="Center"
Margin="40,0,40,0"/>
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
@@ -29,15 +30,15 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Content="Save"
<Button Grid.Column="1" Content="{Binding Save_Msg}"
IsDefault="True"
Command="{Binding Save_Command}"
Visibility="{Binding Buttons_Visibility}"
Style="{StaticResource ToolBar_Button}"/>
<Button Grid.Column="3" Content="Do NOT Save"
Style="{StaticResource EgtWPFLib5_InputButton}"/>
<Button Grid.Column="3" Content="{Binding DoNotSave_Msg}"
Command="{Binding DoNotSave_Command}"
Visibility="{Binding Buttons_Visibility}"
Style="{StaticResource ToolBar_Button}"/>
Style="{StaticResource EgtWPFLib5_InputButton}"/>
</Grid>
</Grid>
@@ -14,13 +14,6 @@ Public Class BlockedWndVM
Private WaitingTimer_TickHandler As New EventHandler(AddressOf WaitingTimer_Tick)
Private m_Msg_Text As String
Public ReadOnly Property Msg_Text As String
Get
Return m_Msg_Text
End Get
End Property
Private m_Buttons_Visibility As Visibility = Visibility.Visible
Public ReadOnly Property Buttons_Visibility As Visibility
Get
@@ -30,6 +23,29 @@ Public Class BlockedWndVM
Private m_nIndex As Integer = 0
#Region "Messages"
Private m_Msg_Text As String
Public ReadOnly Property Msg_Text As String
Get
Return m_Msg_Text
End Get
End Property
Public ReadOnly Property Save_Msg As String
Get
Return EgtMsg(61966)
End Get
End Property
Public ReadOnly Property DoNotSave_Msg As String
Get
Return EgtMsg(61967)
End Get
End Property
#End Region ' Messages
' Definizione comandi
Private m_cmdSave As ICommand
Private m_cmdDoNotSave As ICommand
@@ -341,7 +341,7 @@ Public Class MainMenuVM
End If
' recupero indice di modifica progetto
Dim CommIndex As Integer = -1
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd()
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd(m_SupervisorId)
For Each ActiveSession In ActiveSessionList
If ActiveSession.ItemId = Map.refProdManagerVM.CurrProd.nProdId Then
CommIndex = ActiveSession.Index
@@ -374,13 +374,20 @@ Public Class NestingRunningWndVM
nPartPlaceHolderId = EgtGetNext(nPartPlaceHolderId)
Continue While
End If
Dim frPlaceHolder As New Frame3d
EgtFrame(nPartPlaceHolderId, nRawPartId, frPlaceHolder)
Dim p3Origin As Point3d = New Point3d( frPlaceHolder.Orig.x, frPlaceHolder.Orig.z, 0)
' creo duplo del pezzo
Dim nPartDuploId As Integer = EgtDuploNew(nSourceId)
' elimino valori calcolo dell'originale
MyMachGroupPanelVM.DuploRemoveProjCalc(nPartDuploId)
' annullo rotazioni
Dim b3Duplo As New BBox3d
EgtGetBBoxGlob(nPartDuploId, GDB_BB.STANDARD, b3Duplo)
Dim BTLPart As BTLPartVM = CALCPanelVM.GetBTLPartVMFromBTLPartId(nSourceId)
EgtRotate(nPartDuploId, b3Duplo.Center, Vector3d.X_AX, -BTLPart.nINVERTED)
EgtRotate(nPartDuploId, b3Duplo.Center, -Vector3d.Z_AX, -BTLPart.nROTATED)
' calcolo riferimento
Dim frPlaceHolder As New Frame3d
EgtFrame(nPartPlaceHolderId, nRawPartId, frPlaceHolder)
Dim p3Origin As Point3d = New Point3d(frPlaceHolder.Orig.x, frPlaceHolder.Orig.z, 0)
' lo rendo std
EgtSetMode(nPartDuploId, GDB_MD.STD)
PartList.Add(New NestPartWithFrame(nPartPlaceHolderId, p3Origin, frPlaceHolder, nPartDuploId, nSourceId))
@@ -430,6 +437,15 @@ Public Class NestingRunningWndVM
EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, Vector3d.X_AX, dRot)
EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, Vector3d.Z_AX, dFlip)
ElseIf Map.refProjectVM.BTLStructureVM.nPROJTYPE = BWType.WALL Then
'Dim frPlaceHolder As New Frame3d
'EgtFrame(NestPart.nPartId, GDB_ID.ROOT, frPlaceHolder)
'Dim frDuplo As New Frame3d
'EgtFrame(NestPart.nPartDuploId, GDB_ID.ROOT, frDuplo)
'Dim dDuploRot As Double = GetAngle(frDuplo.VersX, frPlaceHolder.VersX)
'Dim dDuploFlip As Double = GetAngle(frDuplo.VersY, frPlaceHolder.VersY)
'If dDuploFlip > 180 - 10 * EPS_SMALL AndAlso dDuploFlip < 180 + 10 * EPS_SMALL Then
' dDuploRot -= 180
'End If
If NestPart.frFrame.GetEgtType() = Frame3d.TYPE.TOP Then
dFlip = 180
dRot = 180
@@ -445,8 +461,8 @@ Public Class NestingRunningWndVM
Continue For
End If
' faccio rotazioni
EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, -Vector3d.Z_AX, dRot)
EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, Vector3d.X_AX, dFlip)
EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, -Vector3d.Z_AX, dRot)
End If
nPartIndex += 1
EgtSetInfo(nCurrMachGroup, MGR_RPT_PART & nPartIndex,
@@ -567,7 +567,7 @@ Public Class MySceneHostVM
ElseIf Map.refMainMenuVM.SelPage = Pages.MACHINING Then
' recupero indice di modifica progetto quando caricato
Dim CommIndex As Integer = -1
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd()
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd(m_SupervisorId)
For Each ActiveSession In ActiveSessionList
If Not IsNothing(Map.refProdManagerVM.CurrProd) AndAlso ActiveSession.ItemId = Map.refProdManagerVM.CurrProd.nProdId Then
CommIndex = ActiveSession.Index
@@ -21,7 +21,7 @@ Public Class SupervisorComm
End If
m_bTickInExecution = True
' se arriva info di cambio progetto
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd()
Dim ActiveSessionList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetProd(m_SupervisorId)
Dim MySession As StatusMapModel = ActiveSessionList.FirstOrDefault(Function(x) x.Session = DbControllers.m_SupervisorId)
If Not IsNothing(MySession) Then
If m_CurrSession_ProdId <> MySession.ItemId OrElse MySession.Index < m_CurrSession_Index Then
@@ -30,7 +30,7 @@ Public Class SupervisorComm
End If
If MySession.Index > m_CurrSession_Index Then
' recupero elementi modificati
Dim MessagesList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(m_CurrSession_Index + 1)
Dim MessagesList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(m_SupervisorId, m_CurrSession_Index + 1)
For Each Message In MessagesList
Select Case Message.ItemType
Case StatusMapItemType.Comm
@@ -56,7 +56,7 @@ Public Class SupervisorComm
'If IsNothing(Map.refProjManagerVM.CurrProj) OrElse Map.refProjManagerVM.bLoadingProj Then Return
' verifico se c'e' una sessione aperta sul progetto corrente
Dim nModificationIndex As Integer = -1
ActiveSessionList = DbControllers.m_StatusMapController.GetProd()
ActiveSessionList = DbControllers.m_StatusMapController.GetProd(m_SupervisorId)
For Each ActiveSession In ActiveSessionList
If ActiveSession.ItemId = Map.refProjManagerVM.CurrProj.nProdId Then
nModificationIndex = ActiveSession.Index
@@ -69,7 +69,7 @@ Public Class SupervisorComm
' verifico se ci sono modifiche
If nModificationIndex <> Map.refProjManagerVM.CurrProj.nModificationIndex Then
' recupero elementi modificati
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(Map.refProjManagerVM.CurrProj.nModificationIndex + 1)
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(m_SupervisorId, Map.refProjManagerVM.CurrProj.nModificationIndex + 1)
Dim bReloadFile As Boolean = False
If Not IsNothing(MachGroupList) Then
For Each MachGroupModification In MachGroupList
@@ -109,7 +109,7 @@ Public Class SupervisorComm
'If IsNothing(Map.refProdManagerVM.CurrProd) OrElse Map.refProdManagerVM.bLoadingProd Then Return
' verifico se c'e' una sessione aperta sul progetto corrente
Dim nModificationIndex As Integer = -1
ActiveSessionList = DbControllers.m_StatusMapController.GetProd()
ActiveSessionList = DbControllers.m_StatusMapController.GetProd(m_SupervisorId)
For Each ActiveSession In ActiveSessionList
If ActiveSession.ItemId = Map.refProdManagerVM.CurrProd.nProdId Then
nModificationIndex = ActiveSession.Index
@@ -122,7 +122,7 @@ Public Class SupervisorComm
' verifico se ci sono modifiche
If nModificationIndex <> Map.refProdManagerVM.CurrProd.nModificationIndex Then
' recupero elementi modificati
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(Map.refProdManagerVM.CurrProd.nModificationIndex + 1)
Dim MachGroupList As List(Of StatusMapModel) = DbControllers.m_StatusMapController.GetFrom(m_SupervisorId, Map.refProdManagerVM.CurrProd.nModificationIndex + 1)
If Not IsNothing(MachGroupList) Then
Dim bReloadFile As Boolean = False
For Each MachGroupModification In MachGroupList
@@ -189,6 +189,11 @@ Public Class SupervisorComm
BTLPart.NotifyPropertyChanged(NameOf(BTLPart.nDONE))
BTLPart.NotifyPropertyChanged(NameOf(BTLPart.Background))
End If
Case Core.StatusMapOpType.SetPartScrapped
If Not IsNothing(Part) Then
Part.nProduction_State = ItemState.Scrapped
Part.NotifyPropertyChanged(NameOf(Part.Background))
End If
End Select
ElseIf MachGroupModification.ItemType = StatusMapItemType.Comm Then
' se arriva richiesta da supervisore di blocco e salvataggio