using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using ib.essetre.integration; using ib.essetre.integration.egaltech; namespace ib.essetre.integration.egaltech { public class Generator : ib.essetre.integration.IGenerator { public System.Threading.Thread _thread ; public static DataSet ds = new DataSet() ; public static string SqlConnectionStr = "" ; public Generator() { } public void Run(InOutParameters parameters, ICallBack callback) { _thread = new Thread(() => DoJob( parameters, callback)) ; _thread.Start() ; } private delegate void dDoJob( InOutParameters parameters, ICallBack callback) ; // campo data ultima modifica nel DATABASE [lastModified] private static void DoJob( InOutParameters parameters, ICallBack callback) { callback.Progress( 0, "Init") ; //GetToolParams(parameters); //GetMachineParams(parameters); SqlConnectionStr = parameters.ConnectionString ; String ExePath = parameters.Read( "EGALTECH", "PATH_EXE", "") ; String BtlDir = parameters.Read( "EGALTECH", "DIR_BTL", "") ; // Se la directory in cui salvare il file non esiste viene creata DirectoryInfo di = new DirectoryInfo( BtlDir) ; if ( ! di.Exists) di.Create() ; int numBars = parameters.Patterns.Length ; for ( int i = 0; i < numBars; i++) { PatternInfo pattInfo = parameters.Patterns[i] ; BTL btlObject = new BTL() ; // Se il parametro IsFromProject è TRUE viene eseguita la query sulle tabelle Project, // se invece è FALSE viene eseguita la query sulle tabelle Production string sql = "" ; if ( pattInfo.IsFromProject) { sql = "SELECT P.projectId, " + "P.elementId, 0 cutId, processId, info1, info2, enabled, level, " + "inTools, outTools, isChecked, P.done, isoType, ax, ay, az, P.x, P.y, P.z, " + "P.flagDeleted, P.[group], [key], face, edge, des, " + "p01, p02, p03, p04, p05, p06, p07, p08, p09, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, " + "q01, q02, q03, q04, q05, q06, q07, q08, q09, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, " + "priority, processIdent, processingQuality, sag1, P.projectId, P.elementId, P.processId, 0 cutStart, 0 cutEnd, " + "E.inverted, E.rotated, 0 doneTime, 0 startAngle, 0 endAngle, -1 referenceCutId, E.length, E.width, E.height " + "FROM dbo.vw_Process AS P " + "INNER JOIN dbo.vw_Element AS E " + "ON P.projectId=E.projectId AND P.elementId=E.elementId " + "WHERE P.projectId = @firstId AND E.elementId = @secondId" ; } else { sql = "SELECT vw_Task.productionId, " + "vw_Task.patternId, vw_Task.cutId, taskId, info1, info2, enabled, level, " + "inTools, outTools, isChecked, vw_Task.done, isoType, ax, ay, az, vw_Cut.x, vw_Cut.y, vw_Cut.z, " + "vw_Task.flagDeleted, [group], [key], face, edge, des, " + "p01, p02, p03, p04, p05, p06, p07, p08, p09, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, " + "q01, q02, q03, q04, q05, q06, q07, q08, q09, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, " + "priority, processIdent, processingQuality, sag1, projectId, elementId, cutStart, cutEnd, " + "inverted, rotated, doneTime, startAngle, endAngle, referenceCutId, length, width, height " + "FROM dbo.vw_Task " + "INNER JOIN vw_Cut " + "ON (vw_Task.cutId = vw_Cut.cutId AND vw_Task.patternId = vw_Cut.patternId AND vw_Task.productionId = vw_Cut.productionId) " + "WHERE vw_Task.productionId = @firstId AND vw_Task.patternId = @secondId" ; } // Connessione al DB ed esecuzione query using ( SqlConnection cn = new SqlConnection( SqlConnectionStr)) { cn.Open() ; using (SqlCommand cmd = new SqlCommand( sql, cn)) { cmd.Parameters.Add( "@firstId", SqlDbType.Int) ; cmd.Parameters.Add( "@secondId", SqlDbType.Int) ; if ( pattInfo.IsFromProject) { cmd.Parameters["@firstId"].Value = pattInfo.ProjectId ; cmd.Parameters["@secondId"].Value = pattInfo.ElementId ; } else { cmd.Parameters["@firstId"].Value = pattInfo.ProductionId ; cmd.Parameters["@secondId"].Value = pattInfo.PatternId ; } var dataAdapter = new SqlDataAdapter( cmd) ; // Lettura DB e riempimento DataSet ds.Clear() ; dataAdapter.Fill( ds) ; } } // Verifica parametro isFromProject: nel caso Ordine viene ottenuta la lista dei Process, // nel caso Produzione viene ottenuta la lista dei Task if ( pattInfo.IsFromProject) GetListaProcesses( btlObject) ; else GetListaTasks( btlObject) ; // Dialog con Progress Bar float progValue = (float) 1 / numBars ; callback.Progress( progValue * ( i + 1), " Progresso: " + ( progValue * ( i + 1) * 100) + "%" + "\n" + " BTL generati: " + ( i + 1) + " su " + numBars) ; // Scrittura del file BTL // Il nome del file cambia a seconda che si tratti del caso Produzione o caso Ordine string BtlName ; if ( pattInfo.IsFromProject) BtlName = "Part_" + pattInfo.ProjectId.ToString() + "_" + pattInfo.ElementId.ToString() + ".btl" ; else BtlName = "Bar_" + pattInfo.ProductionId.ToString() + "_" + pattInfo.PatternId.ToString() + ".btl" ; // Path completa del nome del file da salvare string BtlPath = Path.Combine( BtlDir, BtlName) ; 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 string path = "\"" + BtlPath + "\" " + parameters.MachineName + " " + (int)parameters.UIMode ; Process process = Process.Start( ExePath, path) ; // Attendo il termine del processo while ( ! process.HasExited) { process.Refresh() ; System.Threading.Thread.Sleep( 50) ; } } // altrimenti scrivo direttamente il file di errore else { // scrivo il file 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( "CUTID=" + ErrCutId.ToString()) ; tw.WriteLine( "TASKID=0") ; } // 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 = "" ; if ( File.Exists( Path.ChangeExtension( BtlPath, ".txt"))) { int nErr = 0 ; string sMsg = "" ; 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( "CUTID=")) { int? nVal = GetVal( line, "CUTID") ; cutId = ( nVal != null ? nVal.Value : 0) ; } else if ( line.StartsWith( "TASKID=")) { int? nVal = GetVal( line, "TASKID") ; taskId = ( nVal != null ? nVal.Value : 0) ; PatternInfo.Results resExe = PatternInfo.Results.OK ; if ( nErr > 0) { resExe = PatternInfo.Results.ERROR ; bErrors = true ; nLastErr = nErr ; sLastMsg = sMsg ; } pattInfo.SetTaskState( cutId, taskId, resExe, sMsg) ; } else if ( ! string.IsNullOrWhiteSpace( line)) sMsg = line ; } } if ( ! bErrors) { pattInfo.Result = PatternInfo.Results.OK ; pattInfo.DetailResult = PatternInfo.DetailResults.MEMORY ; } else { if ( nLastErr != 17) { pattInfo.Result = PatternInfo.Results.ERROR ; pattInfo.DetailResult = PatternInfo.DetailResults.MEMORY ; } else { pattInfo.Result = PatternInfo.Results.ERROR ; pattInfo.DetailResult = PatternInfo.DetailResults.ALL_KO ; pattInfo.Message = sLastMsg ; } } pattInfo.EstimatedTime = 32.7 ; // Lettura OutputFilename e scrittura nel parametro stringa Iso (solo nel caso UIModes.HIDDEN) if ( ! bErrors && parameters.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_KO ; } } } callback.Progress( 1, "End") ; 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 ; } public void Abort() { //non implementata al momento } // Il seguente metodo prende i record visualizzati in ds (Caso: Produzione) e li carica in un oggetto BTL public static void GetListaTasks( BTL btlObject) { DataTable dTab = ds.Tables[0] ; Int32 listaTasksCount = dTab.Rows.Count ; if ( listaTasksCount == 0) return ; DataRow dRow0 = dTab.Rows[0] ; btlObject.projectNumber = dRow0["projectId"].ToString() ; btlObject.scaleUnit = "2" ; btlObject.barLength = Convert.ToString( GetBarLengthP( (int)dRow0["patternId"], (int)dRow0["productionId"])) ; btlObject.productionId = (int)dRow0["productionId"] ; btlObject.patternId = (int)dRow0["patternId"] ; btlObject.parts = new List() ; List ListaCutParts = GetListaCutIdP() ; for ( int c = 0 ; c < ListaCutParts.Count ; c++) { BTL.Part readPart = new BTL.Part() ; bool bFirst = true ; for ( int f = 0; f < listaTasksCount; f++) { DataRow dRowF = dTab.Rows[f] ; if ( (int)dRowF["cutId"] != ListaCutParts[c]) continue ; if ( bFirst) { readPart.singleMemberNumber = dRowF["cutId"].ToString() ; readPart.count = "1" ; readPart.length = dRowF["length"].ToString() ; readPart.height = dRowF["height"].ToString() ; readPart.width = dRowF["width"].ToString() ; readPart.x = (double)dRowF["x"] ; readPart.inverted = dRowF["inverted"].ToString() ; readPart.rotated = dRowF["rotated"].ToString() ; readPart.cutId = (int)dRowF["cutId"] ; bFirst = false ; readPart.features = new List() ; } BTL.Feature readFeature = new BTL.Feature() { taskId = dRowF["taskId"].ToString(), processKey = dRowF["group"].ToString() + "-" + dRowF["key"].ToString().Substring( 1, 3) + "-" + dRowF["face"].ToString(), designation = dRowF["des"].ToString(), processIdent = dRowF["processIdent"].ToString(), sag1 = dRowF["sag1"].ToString(), process = ( Convert.ToInt16( dRowF["enabled"]) == 1) ? "YES" : "NO", processParameters = new List( 46) { dRowF["p01"].ToString(), dRowF["p02"].ToString(), dRowF["p03"].ToString(), dRowF["p04"].ToString(), dRowF["p05"].ToString(), dRowF["p06"].ToString(), dRowF["p07"].ToString(), dRowF["p08"].ToString(), dRowF["p09"].ToString(), dRowF["p10"].ToString(), dRowF["p11"].ToString(), dRowF["p12"].ToString(), dRowF["p13"].ToString(), dRowF["p14"].ToString(), dRowF["p15"].ToString(), dRowF["p16"].ToString(), dRowF["p17"].ToString(), dRowF["p18"].ToString(), dRowF["p19"].ToString(), dRowF["p20"].ToString(), dRowF["p21"].ToString(), dRowF["p22"].ToString(), dRowF["p23"].ToString(), dRowF["p24"].ToString(), dRowF["p25"].ToString(), dRowF["p26"].ToString(), dRowF["q01"].ToString(), dRowF["q02"].ToString(), dRowF["q03"].ToString(), dRowF["q04"].ToString(), dRowF["q05"].ToString(), dRowF["q06"].ToString(), dRowF["q07"].ToString(), dRowF["q08"].ToString(), dRowF["q09"].ToString(), dRowF["q10"].ToString(), dRowF["q11"].ToString(), dRowF["q12"].ToString(), dRowF["q13"].ToString(), dRowF["q14"].ToString(), dRowF["q15"].ToString(), dRowF["q16"].ToString(), dRowF["q17"].ToString(), dRowF["q18"].ToString(), dRowF["q19"].ToString(), dRowF["q20"].ToString() }, } ; readPart.features.Add( readFeature) ; } btlObject.parts.Add( readPart) ; } // L'ordinamento delle Part in un oggetto BTL avviene col confronto del parametro x, estratto dalla tabella vw_Cut btlObject.parts.Sort( (p, q) => p.x.CompareTo( q.x)) ; } // Il seguente metodo prende i record visualizzati in ds (Caso: Ordine) e li carica in un oggetto BTL public static void GetListaProcesses( BTL btlObject) { DataTable dTab = ds.Tables[0] ; int listaProcessesCount = dTab.Rows.Count ; if ( listaProcessesCount == 0) { btlObject.parts = null ; return ; } DataRow dRow0 = dTab.Rows[0] ; btlObject.projectNumber = dRow0["projectId"].ToString() ; btlObject.scaleUnit = " 2" ; btlObject.barLength = Convert.ToString( GetBarLengthO( (int)dRow0["elementId"], (int)dRow0["projectId"])) ; btlObject.projectId = (int)dRow0["projectId"] ; btlObject.elementId = (int)dRow0["elementId"] ; btlObject.parts = new List() ; List ListaCutParts = new List( 1) ; ListaCutParts.Add( (int)dRow0["elementId"]) ; for ( int c = 0 ; c < ListaCutParts.Count ; c++) { BTL.Part readPart = new BTL.Part() ; bool bFirst = true ; for ( int f = 0 ; f < listaProcessesCount ; f++) { DataRow dRowF = dTab.Rows[f] ; if ( (int)dRowF["elementId"] != ListaCutParts[c]) continue ; if ( bFirst) { readPart.singleMemberNumber = dRowF["elementId"].ToString() ; readPart.count = "1" ; readPart.length = dRowF["length"].ToString() ; readPart.height = dRowF["height"].ToString() ; readPart.width = dRowF["width"].ToString() ; readPart.x = Constants.HeadOverMat ; readPart.inverted = dRowF["inverted"].ToString() ; readPart.rotated = dRowF["rotated"].ToString() ; readPart.elementId = (int)dRowF["elementId"] ; bFirst = false ; readPart.features = new List() ; } BTL.Feature readFeature = new BTL.Feature() { processId = dRowF["processId"].ToString(), processKey = dRowF["group"].ToString() + "-" + dRowF["key"].ToString().Substring( 1, 3) + "-" + dRowF["face"].ToString(), designation = dRowF["des"].ToString(), processIdent = dRowF["processIdent"].ToString(), sag1 = dRowF["sag1"].ToString(), process = ( Convert.ToInt16( dRowF["enabled"]) == 1) ? "YES" : "NO", processParameters = new List( 46) { dRowF["p01"].ToString(), dRowF["p02"].ToString(), dRowF["p03"].ToString(), dRowF["p04"].ToString(), dRowF["p05"].ToString(), dRowF["p06"].ToString(), dRowF["p07"].ToString(), dRowF["p08"].ToString(), dRowF["p09"].ToString(), dRowF["p10"].ToString(), dRowF["p11"].ToString(), dRowF["p12"].ToString(), dRowF["p13"].ToString(), dRowF["p14"].ToString(), dRowF["p15"].ToString(), dRowF["p16"].ToString(), dRowF["p17"].ToString(), dRowF["p18"].ToString(), dRowF["p19"].ToString(), dRowF["p20"].ToString(), dRowF["p21"].ToString(), dRowF["p22"].ToString(), dRowF["p23"].ToString(), dRowF["p24"].ToString(), dRowF["p25"].ToString(), dRowF["p26"].ToString(), dRowF["q01"].ToString(), dRowF["q02"].ToString(), dRowF["q03"].ToString(), dRowF["q04"].ToString(), dRowF["q05"].ToString(), dRowF["q06"].ToString(), dRowF["q07"].ToString(), dRowF["q08"].ToString(), dRowF["q09"].ToString(), dRowF["q10"].ToString(), dRowF["q11"].ToString(), dRowF["q12"].ToString(), dRowF["q13"].ToString(), dRowF["q14"].ToString(), dRowF["q15"].ToString(), dRowF["q16"].ToString(), dRowF["q17"].ToString(), dRowF["q18"].ToString(), dRowF["q19"].ToString(), dRowF["q20"].ToString() }, } ; readPart.features.Add( readFeature) ; } btlObject.parts.Add( readPart) ; } // L'ordinamento delle Part in un oggetto BTL avviene col confronto del parametro x, estratto dalla tabella vw_Cut btlObject.parts.Sort( (p, q) => p.x.CompareTo( q.x)) ; } // Il seguente metodo si connette al DB per estrarre tramite una query i cutId della tabella vw_Cut // aventi patternId e ProductionId che corrispondono a quelli passati come argomento private static List GetListaCutIdP() { List ListaCutId = new List() ; DataTable dTab = ds.Tables[0] ; int listaTasksCount = dTab.Rows.Count ; for ( int f = 0 ; f < listaTasksCount ; f++) { int nCut = (int) dTab.Rows[f]["cutId"] ; if ( ! ListaCutId.Contains( nCut)) ListaCutId.Add( nCut) ; } return ListaCutId ; } // Recupera la lunghezza della barra nel caso Produzione (colonna "l") private static double GetBarLengthP( int patternId, int productionId) { double barLength = 0 ; string sqlBarLength = "SELECT l " + "FROM dbo.vw_Pattern " + "WHERE patternId = @firstId " + "AND productionId = @secondId" ; // Connessione al DB ed esecuzione query using ( SqlConnection cn = new SqlConnection( SqlConnectionStr)) { cn.Open() ; using ( SqlCommand sqlCommand = new SqlCommand( sqlBarLength, cn)) { sqlCommand.Parameters.Add( "@firstId", SqlDbType.Int) ; sqlCommand.Parameters.Add( "@secondId", SqlDbType.Int) ; sqlCommand.Parameters["@firstId"].Value = patternId ; sqlCommand.Parameters["@secondId"].Value = productionId ; SqlDataReader reader = sqlCommand.ExecuteReader() ; while ( reader.Read()) { barLength = Convert.ToDouble( reader["l"]) ; } } } return barLength ; } // Recupera la lunghezza della barra nel caso Ordine (colonna "length", ovvero la lunghezza della singola trave) // a cui aggiungiamo un valore fisso (es. 2000) private static double GetBarLengthO( int elementId, int projectId) { double barLength = 0 ; DataTable dTab = ds.Tables[0] ; int listaProcessesCount = dTab.Rows.Count ; if ( listaProcessesCount > 0) barLength = Convert.ToDouble( dTab.Rows[0]["length"]) + Constants.BarAddLen ; return barLength ; } //------------------------------------------------------------------------------------------------------------- // Dati utensili e macchina //------------------------------------------------------------------------------------------------------------- // Import della funzione di sistema per la scrittura su file INI [DllImport("kernel32", CharSet = CharSet.Unicode)] static extern long WritePrivateProfileString( string Section, string Key, string Value, string FilePath) ; // Scrive il file ToolConfig.ini che include gli utensili e i relativi parametri presi dal DB private static void GetToolParams( InOutParameters parameters) { // Direttorio di scrittura del file e sua creazione se non esiste String BtlDir = parameters.Read( "EGALTECH", "DIR_BTL", "") ; DirectoryInfo di = new DirectoryInfo( BtlDir) ; if ( ! di.Exists) di.Create() ; // Path del file INI e sua cancellazione se già esiste string IniPath = Path.Combine( BtlDir, "ToolConfig.ini") ; if ( File.Exists( IniPath)) File.Delete( IniPath) ; // Preparazione interrogazione del DB SqlConnectionStr = parameters.ConnectionString ; DataSet dsTools = new DataSet() ; string sqlTool = "SELECT [vw_Tool].[toolId], [code], [jobType], [toolType], " + "[enabled], [note], [position], [file3ds], [rpm], [feed], [srot], " + "[number], [vw_Tool].[configurationId], [vw_Tool].[flagDeleted], [lastModified], " + "[key], [value] " + "FROM [ESSETRE].[dbo].[vw_Tool] " + "INNER JOIN [ESSETRE].[dbo].[vw_ToolParam] " + "ON [vw_Tool].[toolId] = [vw_ToolParam].[toolId] "; // Connessione al DB ed esecuzione query using ( SqlConnection cn = new SqlConnection( SqlConnectionStr)) { cn.Open() ; using ( SqlCommand cmd = new SqlCommand( sqlTool, cn)) { var dataAdapter = new SqlDataAdapter( cmd) ; // Lettura DB e riempimento DataSet dsTools.Clear() ; dataAdapter.Fill( dsTools) ; } } // Scrittura del file foreach ( DataTable table in dsTools.Tables) { int toolId = 0 ; String Section = "" ; foreach ( DataRow row in table.Rows) { String Key = "" ; String Value = "" ; if ( (int)row["toolId"] != toolId) { // Nuovo utensile toolId = (int)row["toolId"] ; Section = "Tool_" + toolId.ToString("00") ; //row["code"].ToString(); // Dati utensile presenti in ogni riga foreach ( DataColumn column in table.Columns) { if ( ! ( column.ColumnName.Equals( "key") || column.ColumnName.Equals( "value"))) { Key = column.ColumnName ; Value = row[column].ToString() ; WritePrivateProfileString( Section, Key, Value, IniPath) ; } } } // Altri dati utensile Key = row["key"].ToString() ; Value = row["value"].ToString() ; WritePrivateProfileString( Section, Key, Value, IniPath) ; } } } // Scrive il file MachConfig_[MachineName].ini che include i parametri macchina presi dal DB private static void GetMachineParams( InOutParameters parameters) { // Direttorio di scrittura del file e sua creazione se non esiste String BtlDir = parameters.Read( "EGALTECH", "DIR_BTL", "") ; DirectoryInfo di = new DirectoryInfo( BtlDir) ; if ( ! di.Exists) di.Create() ; // Path del file INI e sua cancellazione se già esiste string IniName = "MachConfig_" + parameters.MachineName + ".ini" ; string IniPath = Path.Combine( BtlDir, IniName) ; if ( File.Exists( IniPath)) File.Delete( IniPath) ; // Preparazione interrogazione del DB SqlConnectionStr = parameters.ConnectionString ; DataSet dsMachs = new DataSet() ; string sqlMach = "SELECT [group], [key], [value] " + "FROM [ESSETRE].[dbo].[vw_MachineParam] " + "WHERE [group] = 'OFFSETS' " + "OR [group] = 'TRAVE' "; // Connessione al DB ed esecuzione query using ( SqlConnection cn = new SqlConnection(SqlConnectionStr)) { cn.Open() ; using ( SqlCommand cmd = new SqlCommand(sqlMach, cn)) { var dataAdapter = new SqlDataAdapter( cmd) ; // Lettura DB e riempimento DataSet dsMachs.Clear() ; dataAdapter.Fill(dsMachs) ; } } // Scrittura del file INI foreach ( DataTable table in dsMachs.Tables) { foreach ( DataRow row in table.Rows) { String Section = row["group"].ToString() ; String Key = row["key"].ToString() ; String Value = row["value"].ToString() ; WritePrivateProfileString( Section, Key, Value, IniPath) ; } } } } }