private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            MainWindow mw = (MainWindow)e.Argument;
            FMFileListItem fli;
            int fNum = 0;
            string s;
            try
            {
                foreach (IFilePointSelector fr in FilePointSelectors)//First capture all the data variables to be created
                {
                    for (int i = 0; i < fr.NumberOfFiles; i++)
                        Log.writeToLog("     " + fr[i].path);
                    if (fr.GetType() == typeof(FMFileListItem)) //process FILMAN file
                    {
                        for (int i = 0; i < fr.NumberOfFiles; i++)
                        {
                            systat.AddCommentLine(fr[i].path); //FIRST comment lines name files
                            for (int j = 0; j < 6; j++)
                                systat.AddCommentLine(((FILMANFileRecord)fr[i]).stream.Description(j));
                        }
                        systat.AddCommentLine(new string('*', 72)); //LAST comment line to mark end
                        fli = (FMFileListItem)fr;
                        object[] GVcodes = new object[4] { fli.FileUID, ++fNum, 2, 0 }; //FfGg
                        // F is the FileUID
                        // G is the old GV number from FILMAN
                        // g is the renumbering of GVs
                        foreach (GroupVar gv in fli.GroupVars) //first the GVs
                        {
                            GVcodes[2] = (int)GVcodes[2] + 1;
                            if (gv.IsSel)
                            {
                                GVcodes[3] = (int)GVcodes[3] + 1;
                                s = FMFileListItem.GVNameParser.Encode(GVcodes, gv.namingConvention);
                                SYSTAT.SYSTATFileStream.Variable v;
                                v = new SYSTAT.SYSTATFileStream.Variable(s,
                                    gv.Format == NSEnum.Number ? SYSTAT.SYSTATFileStream.SVarType.Number : SYSTAT.SYSTATFileStream.SVarType.String);
                                systat.AddVariable(v);
                            }
                        }
                        object[] Pcodes = new object[7] { fli.FileUID, fNum, 0, 0, 0, 0, "" }; //FfCcPpN
                        // F is the FileUID
                        // f is the index of file (1-based)
                        // C is the original channel number from FILMAN (1-based)
                        // c is the renumbering of channels (1-based)
                        // P is the original point number from FILMAN (1-based)
                        // p is the renumbering of points (1-based)
                        // N is the channel name
                        foreach (PointGroup pg in fli.PointGroups) //then the data points
                        {
                            foreach (int channel in pg.selectedChannels)
                            {
                                Pcodes[2] = channel + 1;
                                s = ((FILMANFileRecord)fr[0]).stream.ChannelNames(channel);
                                Pcodes[6] = s.Substring(0, Math.Min(s.Length, 11)).Trim().Replace(' ', '_');
                                Pcodes[3] = (int)Pcodes[3] + 1;
                                foreach (int point in pg.selectedPoints)
                                {
                                    Pcodes[4] = point + 1;
                                    Pcodes[5] = (int)Pcodes[5] + 1;
                                    s = FMFileListItem.PointNameParser.Encode(Pcodes, pg.namingConvention);
                                    SYSTAT.SYSTATFileStream.Variable v = new SYSTAT.SYSTATFileStream.Variable(s.Substring(0, Math.Min(12, s.Length)));
                                    systat.AddVariable(v);
                                }
                            }
                        }
                    }
                    else //process CSV variables
                        foreach (CSV.Variable v in ((CSVFileRecord)fr[0]).stream.CSVVariables)
                            if (v.IsSel)
                            {
                                SYSTAT.SYSTATFileStream.Variable var;
                                var = new SYSTAT.SYSTATFileStream.Variable(v.Name, v.Type);
                                systat.AddVariable(var);
                            }
                } //end data variable capture; now we can write the SYSTAT header
                systat.WriteHeader();

                FILMANRecord FMRec;
                int[] recordsPerFile = new int[rowsInColumn(0)];
                for (int i = 0; i < recordsPerFile.Length; i++)
                    recordsPerFile[i] = recsInItem(i, 0);
                int recordNumber = 1;
                int numberOfRecords = FilePointSelectors[0].NumberOfRecords;
                Log.writeToLog("Creating " + numberOfRecords.ToString("0") + " records of " + TotalDataPoints().ToString("0") + " points");
                for (int rowFile = 0; rowFile < recordsPerFile.Length; rowFile++)
                    for (int recNum = 0; recNum < recordsPerFile[rowFile]; recNum++)
                    {
                        int pointNumber = 0;
                        foreach (IFilePointSelector fr in FilePointSelectors)
                        {
                            if (fr.GetType() == typeof(FMFileListItem))
                            {
                                FMFileListItem ffr = (FMFileListItem)fr;
                                FMRec = ((FILMANFileRecord)ffr[rowFile]).stream.read(recNum, 0); //read first channel to get GV values
                                foreach (GroupVar gv in ffr.GroupVars) //include GV values first
                                {
                                    if (gv.IsSel) //is it selected?
                                    {
                                        int GVValue = FMRec.GV[gv.Index]; //GV integer value
                                        if (gv.GVE == null || gv.Format == NSEnum.Number || gv.Format == NSEnum.String)
                                            systat.SetVariableValue(pointNumber++, GVValue);
                                        else //GVE != null & Format == NSEnum.MappingString
                                            systat.SetVariableValue(pointNumber++, gv.GVE.ConvertGVValueIntegerToString(GVValue)); //look up dictionary entry, if any
                                    }
                                }
                                foreach (PointGroup pg in ffr.PointGroups)
                                {
                                    foreach (int chan in pg.selectedChannels)
                                    {
                                        FMRec = ((FILMANFileRecord)ffr[rowFile]).stream.read(recNum, chan);
                                        foreach (int pt in pg.selectedPoints)
                                        {
                                            systat.SetVariableValue(pointNumber++, FMRec[pt]);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                CSVFileRecord cfr = (CSVFileRecord)fr[rowFile];
                                cfr.stream.Read();
                                foreach (CSV.Variable v in cfr.stream.CSVVariables)
                                    if (v.IsSel)
                                        systat.SetVariableValue(pointNumber++, v.Value);
                            }
                        }
                        systat.WriteDataRecord();
                        int prog = Convert.ToInt32(100D * ((double)(recordNumber++)) / ((double)(numberOfRecords)));
                        bw.ReportProgress(prog);
                        if (bw.CancellationPending) //check for abort
                        {
                            e.Cancel = true;
                            return;
                        }
                    }

                systat.CloseStream();
                Log.writeToLog("Finished consolidation");
            }
            catch (Exception err)
            {
                throw new Exception("Source: " + err.Source + " Message: " + err.Message, err);
            }
        }
        private void Create_Click(object sender, RoutedEventArgs e)
        {
            Create.Visibility = Visibility.Hidden;
            Log.writeToLog("Beginning data consolidation to: " + SYSTATFileName.Text);
            FMFileListItem fli;
            SYSTAT.SYSTATFileStream systat = null;
            try
            {
                systat = new SYSTAT.SYSTATFileStream(SYSTATFileName.Text,
                    ((bool)SYS.IsChecked) ? SYSTAT.SYSTATFileStream.SFileType.SYS : SYSTAT.SYSTATFileStream.SFileType.SYD);
                Log.writeToLog("Consolidating from files:");
                int fNum = 0;
                string s;
                foreach (FILMANFileRecord ffr in FILMANFileRecords)//First capture all the data variables to be created
                {
                    Log.writeToLog("     " + ffr.path);
                    systat.AddCommentLine(ffr.path); //FIRST comment line names file
                    for (int i = 0; i < 6; i++)
                        systat.AddCommentLine(ffr.stream.Description(i));
                    systat.AddCommentLine(new string('*', 72)); //LAST comment line to mark end
                    fli = ffr.FMFilePointSelector;
                    object[] GVcodes = new object[4] { fli.FileUID, ++fNum, 2, 0 }; //FfGg
                    // F is the FileUID
                    // G is the old GV number from FILMAN
                    // g is the renumbering of GVs
                    foreach (GroupVar gv in fli.GroupVars) //first the GVs
                    {
                        GVcodes[2] = (int)GVcodes[2] + 1;
                        if (gv.IsSel)
                        {
                            GVcodes[3] = (int)GVcodes[3] + 1;
                            s = FMFileListItem.GVNameParser.Encode(GVcodes, gv.namingConvention);
                            SYSTAT.SYSTATFileStream.Variable v;
                            v = new SYSTAT.SYSTATFileStream.Variable(s,
                                gv.Format == NSEnum.Number ? SYSTAT.SYSTATFileStream.SVarType.Number : SYSTAT.SYSTATFileStream.SVarType.String);
                            systat.AddVariable(v);
                        }
                    }
                    object[] Pcodes = new object[7] { fli.FileUID, fNum, 0, 0, 0, 0, "" }; //FfCcPpN
                    // F is the FileUID
                    // f is the index of file (1-based)
                    // C is the original channel number from FILMAN (1-based)
                    // c is the renumbering of channels (1-based)
                    // P is the original point number from FILMAN (1-based)
                    // p is the renumbering of points (1-based)
                    // N is the channel name
                    foreach (PointGroup pg in fli.PointGroups) //then the data points
                    {
                        foreach (int channel in pg.selectedChannels)
                        {
                            Pcodes[2] = channel + 1;
                            s = ffr.stream.ChannelNames(channel);
                            Pcodes[6] = s.Substring(0, Math.Min(s.Length, 11)).Trim().Replace(' ', '_');
                            Pcodes[3] = (int)Pcodes[3] + 1;
                            foreach (int point in pg.selectedPoints)
                            {
                                Pcodes[4] = point + 1;
                                Pcodes[5] = (int)Pcodes[5] + 1;
                                s = FMFileListItem.PointNameParser.Encode(Pcodes, pg.namingConvention);
                                SYSTAT.SYSTATFileStream.Variable v = new SYSTAT.SYSTATFileStream.Variable(s.Substring(0, Math.Min(12, s.Length)));
                                systat.AddVariable(v);
                            }
                        }
                    }
                }
                foreach (CSVFileRecord cfr in CSVFileRecords) //now get CSV variables
                {
                    Log.writeToLog("     " + cfr.path);
                    foreach (CSV.Variable v in cfr.stream.CSVVariables)
                    {
                        if (v.IsSel)
                        {
                            SYSTAT.SYSTATFileStream.Variable var;
                            var = new SYSTAT.SYSTATFileStream.Variable(v.Name, v.Type);
                            systat.AddVariable(var);
                        }
                    }
                } //end data variable capture; now we can write the SYSTAT header
                systat.WriteHeader();

                FILMANRecord FMRec;
                int numberOfRecords;
                if (FILMANFileRecords.Count > 0)
                    numberOfRecords = FILMANFileRecords[0].stream.NRecordSets;
                else
                    numberOfRecords = CSVFileRecords[0].stream.NumberOfRecords;
                Log.writeToLog("Creating " + numberOfRecords.ToString("0") + " records of " + TotalDataPoints().ToString("0") + " points");
                for (int recordNum = 0; recordNum < numberOfRecords; recordNum++)
                {
                    int pointNumber = 0;
                    foreach (FILMANFileRecord ffr in FILMANFileRecords)
                    {
                        fli = ffr.FMFilePointSelector;
                        FMRec = ffr.stream.read(recordNum, 0); //read first channel to get GV values
                        foreach (GroupVar gv in fli.GroupVars)
                        {
                            if (gv.IsSel)
                            {
                                int GVValue = FMRec.GV[gv.Index];
                                if (gv.GVE == null || gv.Format == NSEnum.Number || gv.Format == NSEnum.String)
                                    systat.SetVariableValue(pointNumber++, GVValue);
                                else //GVE != null & Format == NSEnum.MappingString
                                    systat.SetVariableValue(pointNumber++, gv.GVE.ConvertGVValueIntegerToString(GVValue));
                            }
                        }
                        foreach (PointGroup pg in fli.PointGroups)
                        {
                            foreach (int chan in pg.selectedChannels)
                            {
                                FMRec = ffr.stream.read(recordNum, chan);
                                foreach (int pt in pg.selectedPoints)
                                {
                                    systat.SetVariableValue(pointNumber++, FMRec[pt]);
                                }
                            }
                        }
                    }
                    foreach (CSVFileRecord cfr in CSVFileRecords) //now get CSV variables
                    {
                        cfr.stream.Read();
                        foreach (CSV.Variable v in cfr.stream.CSVVariables)
                            if (v.IsSel)
                                systat.SetVariableValue(pointNumber++, v.Value);
                    }
                    systat.WriteDataRecord();
                }

                systat.CloseStream();
                Log.writeToLog("Finished consolidation");
            }
            catch (Exception err)
            {
                ErrorWindow ew = new ErrorWindow();
                ew.Message = "***** ERROR ***** Source: " + err.Source + " Message: " + err.Message;
                ew.ShowDialog();
                if (systat != null) systat.CloseStream();
            }
            Create.Visibility = Visibility.Visible;
        }