From 500632b79a7deb250f5be986b6c9faf85ad3d22c Mon Sep 17 00:00:00 2001 From: Dario Sassi Date: Sat, 1 Feb 2020 12:39:35 +0000 Subject: [PATCH] Integration 2.2a5 : - aggiunta gestione multitask per EgtCAM5. --- IntegrationEgaltech/Generator.cs | 547 +++++++++++------- .../Properties/AssemblyInfo.cs | 4 +- 2 files changed, 338 insertions(+), 213 deletions(-) diff --git a/IntegrationEgaltech/Generator.cs b/IntegrationEgaltech/Generator.cs index 6966d9e..8c7688f 100644 --- a/IntegrationEgaltech/Generator.cs +++ b/IntegrationEgaltech/Generator.cs @@ -22,6 +22,21 @@ namespace ib.essetre.integration.egaltech private static DataSet _ds = new DataSet() ; private static string _SqlConnectionStr = "" ; + private struct Bar + { + public PatternInfo piInfo ; + public int nBarState ; + public string sBtlPath ; + public bool bBtlOk ; + } + + private struct MyProc + { + public bool bEnable ; + public Process Proc ; + public int nBar ; + } + //------------------------------------------------------------------------------------------------------------- public Generator() { @@ -48,8 +63,19 @@ namespace ib.essetre.integration.egaltech String ExePath = GetExePath( parameters) ; String DataDir = GetDataDir( parameters) ; + // Numero di barre int numBars = parameters.Patterns.Length ; + if ( numBars == 0) + return ; + // Numero di core logici da utilizzare (minimo tra presenti sul PC e imposti da INI) + int nMaxThread = Math.Min( Environment.ProcessorCount, GetMaxInstances( parameters)) ; + + // Frazione di avanzamento del lavoro (in pu) + double dProgress = 0 ; + + // Genero i Btl di tutte le barre + Bar[] vBar = new Bar[ numBars + nMaxThread] ; for ( int i = 0; i < numBars; i++) { PatternInfo pattInfo = parameters.Patterns[i] ; @@ -122,252 +148,143 @@ namespace ib.essetre.integration.egaltech if ( ! pattInfo.IsFromProject) nBarState = GetBarStateP( btlObject.patternId, btlObject.productionId) ; - // Dialog con Progress Bar - float progValue1 = (float) 1 / numBars ; - string sProgress1 = ( progValue1 * ( i + 0.2) * 100).ToString("F1", CultureInfo.InvariantCulture) ; - callback.Progress( progValue1 * ( i + 0.2f), - " Progress: " + sProgress1 + "%" + "\n" + - " Count: " + ( i + 1) + " / " + numBars, + // Dialog con Progress Bar + dProgress += 0.1 / numBars ; + string sProg = ( dProgress * 100).ToString("F1", CultureInfo.InvariantCulture) ; + callback.Progress( (float) dProgress, + " Progress: " + sProg + "%" + "\n", ref bCancel) ; if ( bCancel) { callback.Done( parameters) ; //fine return ; } - // Scrittura del file BTL + // Scrittura del file BTL // Path completa del nome del file da salvare string BtlPath = GetBtlPath( DataDir, pattInfo) ; bool BtlOk = btlObject.WriteIntoFile( BtlPath, pattInfo.IsFromProject) ; - // Se scrittura Btl riuscita, lancio EgtCAM5 - if ( BtlOk) { - // Esecuzione di EgtCAM5 con parametri path BTL, nome macchina e modo - Process process = new Process() ; - process.StartInfo.FileName = ExePath ; - int nMode = (int)parameters.UIMode ; - if ( parameters.UIMode == InOutParameters.UIModes.HIDDEN && nBarState != 0) - nMode = 4 ; - process.StartInfo.Arguments = "\"" + BtlPath + "\" " + parameters.MachineName + " " + nMode.ToString() ; - process.StartInfo.UseShellExecute = false ; - process.Start() ; - // Attendo il termine del processo - while ( ! process.HasExited) { - process.Refresh() ; - Thread.Sleep( 50) ; - } - } - - // altrimenti scrivo direttamente il file di errore - else { - // scrivo il file + // Se scrittura Btl non riuscita + if ( ! BtlOk) { + // scrivo direttamente il file di errore int ErrCutId = ( pattInfo.IsFromProject ? pattInfo.ElementId : pattInfo.PatternId) ; using ( var tw = new StreamWriter( Path.ChangeExtension( BtlPath, ".txt"), false)) { tw.WriteLine( "ERR=10") ; - tw.WriteLine( "BTL vuoto") ; + tw.WriteLine( "BTL without processes") ; tw.WriteLine( "CUTID=" + ErrCutId.ToString()) ; tw.WriteLine( "TASKID=0") ; } - // elimino eventuale file Cn già presente + // elimino eventuale file Cn già presente if ( parameters.UIMode == InOutParameters.UIModes.HIDDEN) { string CncPath = Path.ChangeExtension( BtlPath, ".cnc") ; if ( File.Exists( CncPath)) File.Delete( CncPath) ; } } - - // Recupero il risultato - bool bErrors = false ; - int nLastErr = 0 ; - string sLastMsg = "" ; - int nCurrCutId = 0 ; - Dictionary dctErr = new Dictionary() ; - Dictionary dctRot = new Dictionary() ; - int nTotTime = 0 ; - if ( parameters.UIMode != InOutParameters.UIModes.SHOWUI && - File.Exists( Path.ChangeExtension( BtlPath, ".txt"))) { - int nErr = 0 ; - string sMsg = "" ; - double dRot = 0 ; - int cutId = 0 ; - int taskId = 0 ; - string[] lines = System.IO.File.ReadAllLines( Path.ChangeExtension( BtlPath, ".txt")) ; - foreach ( string line in lines) { - if ( line.StartsWith( "ERR=")) { - int? nVal = GetVal(line, "ERR") ; - nErr = ( nVal != null ? nVal.Value : 0) ; - sMsg = "" ; - cutId = 0 ; - taskId = 0 ; - } - else if ( line.StartsWith( "ROT=")) { - int? nVal = GetVal( line, "ROT") ; - int nRot = ( nVal != null ? nVal.Value : 0) ; - dRot = (( 4 - nRot) % 4) * 90 ; - } - else if ( line.StartsWith( "CUTID=")) { - int? nVal = GetVal( line, "CUTID") ; - cutId = ( nVal != null ? nVal.Value : 0) ; - if ( cutId != nCurrCutId) { - nCurrCutId = cutId ; - dctErr.Clear() ; - dctRot.Clear() ; - } - } - else if ( line.StartsWith( "TASKID=")) { - int? nVal = GetVal( line, "TASKID") ; - taskId = ( nVal != null ? nVal.Value : 0) ; - PatternInfo.Results resExe = PatternInfo.Results.OK ; - if ( nErr == 22) { - resExe = PatternInfo.Results.COLLISION ; - bErrors = true ; - // se non assegnata a feature la dichiaro globale - if ( cutId == 0 || taskId == 0) { - nLastErr = nErr ; - sLastMsg = sMsg ; - } - } - else if ( nErr > 0) { - resExe = PatternInfo.Results.WARNING ; - bErrors = true ; - // se non assegnata a feature la dichiaro globale - if ( cutId == 0 || taskId == 0) { - nLastErr = nErr ; - sLastMsg = sMsg ; - } - } - // gestione errore per feature identificata - if ( taskId != 0) { - // verifiche per feature con più parti - if ( dctErr.TryGetValue( taskId, out PatternInfo.Results resCurrExe)) { - if ( resExe > resCurrExe) { - dctErr[taskId] = resExe ; - pattInfo.SetTaskState( cutId, taskId, resExe, sMsg) ; - } - } - else { - dctErr[taskId] = resExe ; - pattInfo.SetTaskState( cutId, taskId, resExe, sMsg) ; - } - } - // altrimenti per feature generica - else { - pattInfo.SetTaskState( cutId, taskId, resExe, sMsg) ; - } - // gestione rotazione per feature identificata - if ( taskId != 0) { - // verifiche per feature con più parti - if ( dctRot.TryGetValue( taskId, out double dCurrRot)) { - if ( Math.Abs( dRot) > 1) { - if ( Math.Abs( dCurrRot) < 1) - dctRot[taskId] = dRot ; - } - else { - dRot = dCurrRot ; - } - } - else { - dctRot.Add( taskId, dRot) ; - } - } - // imposto la rotazione - if ( pattInfo.IsFromProject) - pattInfo.SetProcessInfoChosen( cutId, taskId, dRot, 0, false) ; - else - pattInfo.SetTaskInfoChosen( cutId, taskId, dRot, 0, false) ; - } - else if ( line.StartsWith( "TIME=")) { - int? nVal = GetVal( line, "TIME") ; - nTotTime = ( nVal != null ? nVal.Value : 0) ; - } - else if ( ! string.IsNullOrWhiteSpace( line) && line != "---") - sMsg = line ; - } - } - pattInfo.IsDone = true ; - if ( ! bErrors) { - pattInfo.Result = PatternInfo.Results.OK ; - pattInfo.DetailResult = PatternInfo.DetailResults.MEMORY ; - } + // altrimenti else { - // Sezione oltre i limiti - if ( nLastErr == 17) { - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_WARNING ; - pattInfo.Message = sLastMsg ; - } - // Errore nel calcolo lavorazione o movimento carrelli - else if ( nLastErr == 19) { - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; - pattInfo.Message = sLastMsg ; - } - // Collisione non assegnata ad una feature - else if ( nLastErr == 22) { - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_COLLISION ; - pattInfo.Message = sLastMsg ; - } - // Extra corsa assi - else if ( nLastErr == 23) { - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; - pattInfo.Message = sLastMsg ; - } - // Carri che scivolano sulla trave - else if ( nLastErr == 24) { - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; - pattInfo.Message = sLastMsg ; - } - // Errore generico - else if ( nLastErr == 25) { - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; - pattInfo.Message = sLastMsg ; - } - // Altro - else { - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.MEMORY ; - } + // cancello eventuale file di errore già presente + string TxtPath = Path.ChangeExtension( BtlPath, ".txt") ; + if ( File.Exists( TxtPath)) + File.Delete( TxtPath) ; } - pattInfo.EstimatedTime = nTotTime ; - // Lettura OutputFilename e scrittura nel parametro stringa Iso (caso UIModes.HIDDEN) - if ( ! bErrors && - parameters.UIMode == InOutParameters.UIModes.HIDDEN) { + // Salvo dati + vBar[i].piInfo = pattInfo ; + vBar[i].nBarState = nBarState ; + vBar[i].sBtlPath = BtlPath ; + vBar[i].bBtlOk = BtlOk ; + } - string CncPath = Path.ChangeExtension( BtlPath, ".cnc") ; - if ( File.Exists( CncPath)) { - string lines = System.IO.File.ReadAllText(CncPath) ; - pattInfo.Iso = lines ; - pattInfo.Result = PatternInfo.Results.OK ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_OK ; + // Lancio in parallelo più processi (senza superare il numero di core logici presenti) + MyProc[] vProc = new MyProc[nMaxThread] ; + for ( int j = 0 ; j < nMaxThread ; ++ j) { + vProc[j].nBar = -1 ; + vProc[j].bEnable = true ; + } + + // Processo i Btl in parallelo + int nCurrBar = 0 ; + int nDoneBar = 0 ; + int nShiftBar = 0 ; + int nActProc = 0 ; + while ( nCurrBar < numBars + nShiftBar || nActProc > 0) { + for ( int j = 0 ; j < nMaxThread ; ++ j) { + if ( ! vProc[j].bEnable) + continue ; + bool bDone = false ; + if ( vProc[j].nBar == -1) { + if ( nCurrBar < numBars + nShiftBar) { + if ( vBar[nCurrBar].bBtlOk) { + vProc[j].Proc = new Process() ; + vProc[j].Proc.StartInfo.FileName = ExePath ; + int nMode = (int)parameters.UIMode ; + if ( parameters.UIMode == InOutParameters.UIModes.HIDDEN && vBar[nCurrBar].nBarState != 0) + nMode = 4 ; + vProc[j].Proc.StartInfo.Arguments = "\"" + vBar[nCurrBar].sBtlPath + "\" " + parameters.MachineName + " " + nMode.ToString() ; + vProc[j].Proc.StartInfo.UseShellExecute = false ; + if ( vProc[j].Proc.Start()) { + vProc[j].nBar = nCurrBar ; + nCurrBar ++ ; + nActProc ++ ; + } + } + else { + ProcessResults( vBar[nCurrBar].sBtlPath, vBar[nCurrBar].piInfo, parameters.UIMode) ; + bDone = true ; + nCurrBar ++ ; + } + } } else { - pattInfo.Iso = "" ; - pattInfo.Result = PatternInfo.Results.ERROR ; - pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; + if ( vProc[j].Proc.HasExited) { + // se terminato con successo + if ( vProc[j].Proc.ExitCode == 0) { + // salvo il risultato + ProcessResults( vBar[vProc[j].nBar].sBtlPath, vBar[vProc[j].nBar].piInfo, parameters.UIMode) ; + bDone = true ; + vProc[j].nBar = -1 ; + nActProc -- ; + } + // altrimenti + else { + // aggiungo il pezzo in coda + if ( numBars + nShiftBar < numBars + nMaxThread) { + vBar[numBars + nShiftBar] = vBar[vProc[j].nBar] ; + nShiftBar ++ ; + } + // disabilito il processo + vProc[j].bEnable = false ; + vProc[j].nBar = -1 ; + nActProc -- ; + } + } + else + vProc[j].Proc.Refresh() ; } + if ( bDone) { + // Dialog con Progress Bar + dProgress += ( 0.9 / numBars) ; + nDoneBar ++ ; + string sProg = ( dProgress * 100 ).ToString("F1", CultureInfo.InvariantCulture) ; + callback.Progress((float)dProgress, + " Progress: " + sProg + "%" + "\n" + + " Count: " + nDoneBar + " / " + numBars, + ref bCancel); + if ( bCancel ) { + callback.Done( parameters) ; // fine + return; + } + } + Thread.Sleep( 1) ; } + Thread.Sleep( 10) ; } callback.Progress( 1, "End", ref bCancel) ; Thread.Sleep( 300) ; - callback.Done( parameters) ; //fine - } - - //------------------------------------------------------------------------------------------------------------- - private static int? GetVal( string sText, string sKey) - { - string[] sParts = sText.Split( new Char[] { '=' }) ; - if ( string.Compare( sParts[0], sKey) != 0) - return null ; - if ( ! int.TryParse( sParts[1], out int nVal)) - return null ; - else - return nVal ; + callback.Done( parameters) ; // fine } //------------------------------------------------------------------------------------------------------------- @@ -376,6 +293,191 @@ namespace ib.essetre.integration.egaltech //non implementata al momento } + //------------------------------------------------------------------------------------------------------------- + private static void ProcessResults( string BtlPath, PatternInfo pattInfo, InOutParameters.UIModes UIMode) + { + if ( UIMode == InOutParameters.UIModes.SHOWUI || UIMode == InOutParameters.UIModes.SIMULAZIONE) + return ; + bool bErrors = false ; + int nLastErr = 0 ; + string sLastMsg = "" ; + int nCurrCutId = 0 ; + Dictionary dctErr = new Dictionary() ; + Dictionary dctRot = new Dictionary() ; + int nTotTime = 0 ; + if ( File.Exists( Path.ChangeExtension( BtlPath, ".txt"))) { + int nErr = 0 ; + string sMsg = "" ; + double dRot = 0 ; + int cutId = 0 ; + int taskId = 0 ; + string[] lines = System.IO.File.ReadAllLines( Path.ChangeExtension( BtlPath, ".txt")) ; + foreach ( string line in lines) { + if ( line.StartsWith( "ERR=")) { + int? nVal = GetVal(line, "ERR") ; + nErr = ( nVal != null ? nVal.Value : 0) ; + sMsg = "" ; + cutId = 0 ; + taskId = 0 ; + } + else if ( line.StartsWith( "ROT=")) { + int? nVal = GetVal( line, "ROT") ; + int nRot = ( nVal != null ? nVal.Value : 0) ; + dRot = (( 4 - nRot) % 4) * 90 ; + } + else if ( line.StartsWith( "CUTID=")) { + int? nVal = GetVal( line, "CUTID") ; + cutId = ( nVal != null ? nVal.Value : 0) ; + if ( cutId != nCurrCutId) { + nCurrCutId = cutId ; + dctErr.Clear() ; + dctRot.Clear() ; + } + } + else if ( line.StartsWith( "TASKID=")) { + int? nVal = GetVal( line, "TASKID") ; + taskId = ( nVal != null ? nVal.Value : 0) ; + PatternInfo.Results resExe = PatternInfo.Results.OK ; + if ( nErr == 22) { + resExe = PatternInfo.Results.COLLISION ; + bErrors = true ; + // se non assegnata a feature la dichiaro globale + if ( cutId == 0 || taskId == 0) { + nLastErr = nErr ; + sLastMsg = sMsg ; + } + } + else if ( nErr > 0) { + resExe = PatternInfo.Results.WARNING ; + bErrors = true ; + // se non assegnata a feature la dichiaro globale + if ( cutId == 0 || taskId == 0) { + nLastErr = nErr ; + sLastMsg = sMsg ; + } + } + // gestione errore per feature identificata + if ( taskId != 0) { + // verifiche per feature con più parti + if ( dctErr.TryGetValue( taskId, out PatternInfo.Results resCurrExe)) { + if ( resExe > resCurrExe) { + dctErr[taskId] = resExe ; + pattInfo.SetTaskState( cutId, taskId, resExe, sMsg) ; + } + } + else { + dctErr[taskId] = resExe ; + pattInfo.SetTaskState( cutId, taskId, resExe, sMsg) ; + } + } + // altrimenti per feature generica + else { + pattInfo.SetTaskState( cutId, taskId, resExe, sMsg) ; + } + // gestione rotazione per feature identificata + if ( taskId != 0) { + // verifiche per feature con più parti + if ( dctRot.TryGetValue( taskId, out double dCurrRot)) { + if ( Math.Abs( dRot) > 1) { + if ( Math.Abs( dCurrRot) < 1) + dctRot[taskId] = dRot ; + } + else { + dRot = dCurrRot ; + } + } + else { + dctRot.Add( taskId, dRot) ; + } + } + // imposto la rotazione + if ( pattInfo.IsFromProject) + pattInfo.SetProcessInfoChosen( cutId, taskId, dRot, 0, false) ; + else + pattInfo.SetTaskInfoChosen( cutId, taskId, dRot, 0, false) ; + } + else if ( line.StartsWith( "TIME=")) { + int? nVal = GetVal( line, "TIME") ; + nTotTime = ( nVal != null ? nVal.Value : 0) ; + } + else if ( ! string.IsNullOrWhiteSpace( line) && line != "---") + sMsg = line ; + } + } + else { + bErrors = true ; + nLastErr = 25 ; + sLastMsg = "Execution Error" ; + } + pattInfo.IsDone = true ; + if ( ! bErrors) { + pattInfo.Result = PatternInfo.Results.OK ; + pattInfo.DetailResult = PatternInfo.DetailResults.MEMORY ; + } + else { + // Sezione oltre i limiti + if ( nLastErr == 17) { + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_WARNING ; + pattInfo.Message = sLastMsg ; + } + // Errore nel calcolo lavorazione o movimento carrelli + else if ( nLastErr == 19) { + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; + pattInfo.Message = sLastMsg ; + } + // Collisione non assegnata ad una feature + else if ( nLastErr == 22) { + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_COLLISION ; + pattInfo.Message = sLastMsg ; + } + // Extra corsa assi + else if ( nLastErr == 23) { + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; + pattInfo.Message = sLastMsg ; + } + // Carri che scivolano sulla trave + else if ( nLastErr == 24) { + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; + pattInfo.Message = sLastMsg ; + } + // Errore generico + else if ( nLastErr == 25) { + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; + pattInfo.Message = sLastMsg ; + } + // Altro + else { + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.MEMORY ; + } + } + pattInfo.EstimatedTime = nTotTime ; + + // Lettura OutputFilename e scrittura nel parametro stringa Iso (caso UIModes.HIDDEN) + if ( ! bErrors && + UIMode == InOutParameters.UIModes.HIDDEN) { + + string CncPath = Path.ChangeExtension( BtlPath, ".cnc") ; + if ( File.Exists( CncPath)) { + string lines = System.IO.File.ReadAllText(CncPath) ; + pattInfo.Iso = lines ; + pattInfo.Result = PatternInfo.Results.OK ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_OK ; + } + else { + pattInfo.Iso = "" ; + pattInfo.Result = PatternInfo.Results.ERROR ; + pattInfo.DetailResult = PatternInfo.DetailResults.ALL_ERROR ; + } + } + } + //------------------------------------------------------------------------------------------------------------- // Il seguente metodo prende i record visualizzati in ds (Caso: Produzione) e li carica in un oggetto BTL public static void GetListaTasks( BTL btlObject) @@ -487,6 +589,18 @@ namespace ib.essetre.integration.egaltech btlObject.parts.Sort( (p, q) => p.x.CompareTo( q.x)) ; } + //------------------------------------------------------------------------------------------------------------- + private static int? GetVal( string sText, string sKey) + { + string[] sParts = sText.Split( new Char[] { '=' }) ; + if ( string.Compare( sParts[0], sKey) != 0) + return null ; + if ( ! int.TryParse( sParts[1], out int nVal)) + return null ; + else + return nVal ; + } + //------------------------------------------------------------------------------------------------------------- // Il seguente metodo prende i record visualizzati in ds (Caso: Ordine) e li carica in un oggetto BTL public static void GetListaProcesses( BTL btlObject) @@ -768,6 +882,17 @@ namespace ib.essetre.integration.egaltech return Path.Combine( BtlDir, BtlName) ; } + //------------------------------------------------------------------------------------------------------------- + // Restituisce il massimo numero di istanze di EgtCAM5 eseguibili contemporaneamente + private static int GetMaxInstances( InOutParameters parameters) + { + String sMaxInst = parameters.Read( "EGALTECH", "MaxInstances", "1") ; + if ( ! int.TryParse( sMaxInst, out int nVal)) + return 1 ; + else + return Math.Max( nVal, 1) ; + } + //------------------------------------------------------------------------------------------------------------- // Visualizzazione finestra gestione DB utensili public bool ShowTools( InOutParameters parameters) diff --git a/IntegrationEgaltech/Properties/AssemblyInfo.cs b/IntegrationEgaltech/Properties/AssemblyInfo.cs index 9a94f2f..20a715a 100644 --- a/IntegrationEgaltech/Properties/AssemblyInfo.cs +++ b/IntegrationEgaltech/Properties/AssemblyInfo.cs @@ -36,5 +36,5 @@ using System.Runtime.InteropServices; // È possibile specificare tutti i valori oppure impostare valori predefiniti per i numeri relativi alla revisione e alla build // usando l'asterisco '*' come illustrato di seguito: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.2.1.4")] -[assembly: AssemblyFileVersion("2.2.1.4")] +[assembly: AssemblyVersion("2.2.1.5")] +[assembly: AssemblyFileVersion("2.2.1.5")]