C#实现解析示波器波形文件(trc、dat文件)

之前的文章中对示波器波形文件进行了解析,见参考链接示波器波形数据文件(trc,dat文件)解析。其实,我们对trc或者dat文件的解析主要是通过上位机,对数据进行我们想要的处理,因此,本文我们将一下用C#实现文件的解析。

之前的文章中,我们知道,trc文件可以用十六进制的格式打开,然后进行每个Byte的解析。dat文件可以通过文件流的形式打开,即ASCII的形式打开。因此,对于trc文件我们主要使用BinaryReader来实现解析,dat文件通过StreamReader打开即可。

//打开trc文件
FileStream bin_stream = new FileStream(file_name,FileMode.Open);
BinaryReader bin_reader = new BinaryReader(bin_stream);
//打开dat文件
StreamReader read_file = new StreamReader(file_name);

对于十六进制文件格式的操作,可以具体参考我之前的文章。C#学习随笔—操作BIN文件(读,写,替代),注意:BIN文件的操作实质上也是十六进制码的操作。我这里大概列一下简单的读操作:

bin_reader.ReadBytes(346)//读取346个bytes

对于文本文件格式的操作,基本如下:

read_file.ReadLine();//读取一行String字符串

上面是比较基本的文件操作方法,我们本文不做过多叙述,只说我们会用到的。

然后在解析文件过程中,会用到byte[]转换为float型,byte[]转换为word(int16),byte[]转换为ASCII string,byte转换为ASCII。

BitConverter.ToUInt32(数组名,数组的开始的index);//byte[]转换为Uint32
BitConverter.ToInt16(数组名,数组的开始的index);//byte[]转换为Int16
BitConverter.ToSingle(数组名,数组开始的index);//byte[]转换为Float
Encoding.ASCII.GetString(数组名,数组开始的index,长度);//Byte[]转换为ASCII String字符
(char);//Byte转换为ASCII码,直接强制数据类型转换

然后我们要将数据类型显示到Winform的Chart图表中。

Chart图标的操作,我在这儿不做详细叙述,我这里也只是对数据的初步显示,不做炫酷的操作。效果图如下所示:

直接上源码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace Cellwise_Debug_Tool
{
    public partial class SoftwareToolWindows : Form
    {
      //TRC 文件各个数据类型占用字节数
        public const int LONG_LENGTH = 4;
        public const int WORD_LENGTH = 2;
        public const int FLOAT_LENGTH = 4;
        public const int DOUBLE_LENGTH = 8;
        public const int BYTE_LENGTH = 1;

      //Chart控件中显示的最大数据点数
        public const int MAX_SHOW_LENGTH = 100000;

      //trc文件的数据解析,具体可以参考上一篇文章
        public enum trc_file_offset
        {
            KEY_WORD_OFFSET = 0x000B,
            TEMPLETE_VERSION_OFFSET = 0x001B,
            DATA_TYPE_OFFSET= 0x002B,
            DATA_HIGH_LOW_OFFSET = 0x002D,
            WAVEDESC_LENGTH_OFFSET = 0x002F,
            USERTEXT_LENGTH_OFFSET = 0x0033,
            TRIGTIME_LENGTH_OFFSET = 0x003B,
            RISTIME_LENGTH_OFFSET = 0x003F,
            DATA_ARRAY_1_OFFSET = 0x0047,
            DATA_ARRAY_2_OFFSET = 0x004B,
            INSTRUMENT_NAME_OFFSET = 0x0057,
            INSTRUMENT_NUM_OFFSET = 0x0067,
            CHANNEL_LABEL_OFFSET = 0x006B,
            DATA_ARRAY_1_POINT_LENGTH_OFFSET = 0x007F,
            OSC_DISPLAY_POINT_LENGTH_OFFSET = 0x0083,
            FIRST_VALID_INDEX_OFFSET = 0x0087,
            LAST_VALID_INDEX_OFFSET = 0x008B,
            VERTICAL_GAIN_OFFSET = 0x00A7,
            VERTICAL_OFFSET_OFFSET = 0x00AB,
            HORIZ_INTERVAL_OFFSET = 0x00BB,
            VERTICAL_UNIT_OFFSET = 0x00CF,
            HORIZ_UNIT_OFFSET = 0x00FF,
            TIMEBASE_OFFSET = 0x014F,
            PROBE_RES_SETTING_OFFSET = 0x0151,
            VERTICAL_DIV_OFFSET = 0x0157,
            BANDLIMIT_ENABLE_OFFSET = 0x0159,
            CHANNEL_NUM_OFFSET = 0x0163,
            MAX_WAVEDESC_OFFSET = 0x164,
        };

        public enum message_info
        {
            FORMAT_ERROR,
            STRING_OUT,
        }
        public struct software_status_t
        {
            public byte[] header_byte;
            public byte point_type;//00-->Byte,01-->Word
            public UInt32 user_text_length;
            public UInt32 trig_time_length;
            public UInt32 ris_time_length;
            public UInt32 point_length;
            public float horiz_interval;
            public byte horiz_unit;
            public byte vertical_unit;
            public float vertical_gain;
            public float vertical_offset;
            
            public DataTable data_value;
        }

         software_status_t software_status;

        public SoftwareToolWindows()
        {
            InitializeComponent();
            app_init();
        }

      //用户配置初始化
        private void app_init()
        {
            software_status.header_byte = new byte[512];
            ChartInit();
        }

      //Chart控件初始化
        private void ChartInit()
        {
            #region SetChartProperty
            Waveform.BackColor = Color.White;
            Waveform.BackGradientStyle = System.Windows.Forms.DataVisualization.Charting.GradientStyle.None;
            Waveform.BorderlineColor = Color.Black;
            Waveform.BorderlineDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Solid;
            Waveform.BorderlineWidth = 1;
            Waveform.BorderSkin.SkinStyle = System.Windows.Forms.DataVisualization.Charting.BorderSkinStyle.None;
            #endregion

            #region SetTitleProperty
            System.Windows.Forms.DataVisualization.Charting.Title title = new System.Windows.Forms.DataVisualization.Charting.Title();
            title.Text = "Waveform";
            title.Font = new Font("Times New Roman", 10, FontStyle.Regular);
            title.Alignment = ContentAlignment.TopCenter;
            title.TextStyle = System.Windows.Forms.DataVisualization.Charting.TextStyle.Default;
            Waveform.Titles.Add(title);
            #endregion

            #region SetChartAreaProperty
            //handle in the Form
            #endregion

            #region SetLegandProperty
            //handle in the Form
            #endregion

        }

        //Trc Dat File 对话框处理函数
        private void FileBrowse_Click(object sender, EventArgs e)
        {
            OpenFileDialog open_file = new OpenFileDialog();
            open_file.Filter = "波形文件|*.trc|数据文件|*.dat";//只需要trc文件或者dat文件
            if (open_file.ShowDialog() == DialogResult.OK)
            {
                FilePath.Text = open_file.FileName;
                if (open_file.FilterIndex == 1)//波形数据文件 .trc文件
                {
                    trc_file_convert_to_data(open_file.FileName,Waveform);
                }
                else if(open_file.FilterIndex == 2)//波形数据文件  .dat
                {
                    dat_file_convert_to_data(open_file.FileName, Waveform);
                }
            }
        }

      //trc文件解析函数
      //file_name trc文件路径
      //Chart Chart控件,trc文件数据输出至Chart控件
        private void trc_file_convert_to_data(string file_name,System.Windows.Forms.DataVisualization.Charting.Chart dataChart)
        {
            FileStream bin_stream = new FileStream(file_name,FileMode.Open);
            BinaryReader bin_reader = new BinaryReader(bin_stream);

            try
            {
                software_status.header_byte = bin_reader.ReadBytes((int)trc_file_offset.MAX_WAVEDESC_OFFSET+1);
                string trc_file_log = System.Text.Encoding.ASCII.GetString(software_status.header_byte,(int)trc_file_offset.KEY_WORD_OFFSET,8);
                if (trc_file_log == "WAVEDESC")
                {
                    message_info_output(false, message_info.STRING_OUT, "FILE LOG: WAVEDESC");
                    string trc_file_version = System.Text.Encoding.ASCII.GetString(software_status.header_byte, (int)trc_file_offset.TEMPLETE_VERSION_OFFSET, 10);
                    if (trc_file_version == "LECROY_2_3")
                    {
                        message_info_output(false, message_info.STRING_OUT, "FILE Version: Lecroy_2_3");
                        message_info_output(false, message_info.STRING_OUT, "INSTRUMENT NAME: "+Encoding.ASCII.GetString(software_status.header_byte, (int)trc_file_offset.INSTRUMENT_NAME_OFFSET, 14));
                        message_info_output(false, message_info.STRING_OUT, "INSTRUMENT NUMBER: " + (BitConverter.ToUInt32(software_status.header_byte,(int)trc_file_offset.INSTRUMENT_NUM_OFFSET)).ToString());
                        software_status.point_length = BitConverter.ToUInt32(software_status.header_byte, (int)trc_file_offset.DATA_ARRAY_1_POINT_LENGTH_OFFSET);
                        software_status.horiz_interval = BitConverter.ToSingle(software_status.header_byte, (int)trc_file_offset.HORIZ_INTERVAL_OFFSET);
                        software_status.horiz_unit = software_status.header_byte[(int)trc_file_offset.HORIZ_UNIT_OFFSET];
                        software_status.vertical_unit = software_status.header_byte[(int)trc_file_offset.VERTICAL_OFFSET_OFFSET];
                        software_status.vertical_gain = BitConverter.ToSingle(software_status.header_byte, (int)trc_file_offset.VERTICAL_GAIN_OFFSET);
                        software_status.vertical_offset = BitConverter.ToSingle(software_status.header_byte, (int)trc_file_offset.VERTICAL_OFFSET_OFFSET);


                        message_info_output(false, message_info.STRING_OUT, "DATA POINT LENGTH: " + software_status.point_length.ToString());
                        message_info_output(false, message_info.STRING_OUT, "HORIZ INTERVAL: " + software_status.horiz_interval.ToString("F15")+((char)software_status.horiz_unit).ToString());

                        software_status.user_text_length = BitConverter.ToUInt32(software_status.header_byte, (int)trc_file_offset.USERTEXT_LENGTH_OFFSET);
                        software_status.trig_time_length = BitConverter.ToUInt32(software_status.header_byte, (int)trc_file_offset.TRIGTIME_LENGTH_OFFSET);
                        software_status.ris_time_length = BitConverter.ToUInt32(software_status.header_byte, (int)trc_file_offset.RISTIME_LENGTH_OFFSET);

                        if (software_status.user_text_length > 0)
                        {
                            bin_reader.ReadBytes((int)software_status.user_text_length);
                        }
                        else
                        {
                            message_info_output(false, message_info.STRING_OUT, "USER_TEXT AREA is Empty!");
                        }

                        if (software_status.trig_time_length > 0)
                        {
                            bin_reader.ReadBytes((int)software_status.trig_time_length);
                        }
                        else
                        {
                            message_info_output(false, message_info.STRING_OUT, "TRIG_TIME AREA is Empty!");
                        }

                        if (software_status.ris_time_length > 0)
                        {
                            bin_reader.ReadBytes((int)software_status.ris_time_length);
                        }
                        else
                        {
                            message_info_output(false, message_info.STRING_OUT, "RIS_TIME AREA is Empty!");
                        }

                        software_status.point_type = software_status.header_byte[(int)trc_file_offset.DATA_TYPE_OFFSET];
                        software_status.data_value = new DataTable();
                        software_status.data_value.Columns.Add("Times", typeof(float));
                        software_status.data_value.Columns.Add("Value", typeof(float));
                        DataRow row_value;
                        int y_value=0;
                        byte[] y_value_byte = new byte[2];
                        float finial_value;
                        
                        for (int temp = 0; (temp < MAX_SHOW_LENGTH)&&(temp<software_status.point_length); temp++)
                        {
                            row_value = software_status.data_value.NewRow();
                            row_value["Times"] = temp * software_status.horiz_interval;
                            if (software_status.point_type == 0x00)
                            {
                                y_value = bin_reader.ReadByte();
                            }
                            else if (software_status.point_type == 0x01)
                            {
                                y_value_byte = bin_reader.ReadBytes(2);
                                y_value = BitConverter.ToInt16(y_value_byte, 0);
                            }

                            finial_value = y_value * software_status.vertical_gain - software_status.vertical_offset;
                            row_value["Value"] = finial_value;
                            software_status.data_value.Rows.Add(row_value);
                            Waveform.Series[0].Points.AddXY((temp * software_status.horiz_interval), finial_value);
                        }
                        message_info_output(false, message_info.STRING_OUT, "Load DataTable Finished!");
                    }
                    else
                    {
                        message_info_output(true, message_info.STRING_OUT, "FILE Version:Lecroy_2_3,this tool don't support this version!");
                    }
                }
                else
                {
                    message_info_output(true, message_info.STRING_OUT, "FILE LOG ERROR! FILE LOG:"+trc_file_log);
                }
            }
            catch
            {
                MessageBox.Show("File Load Failed!");
            }
            finally
            {
                bin_reader.Close();
                bin_stream.Close();
            }
        }

      //dat文件解析函数
      //file_name:dat	文件路径
      //Chart Chart控件,dat文件数据输出至Chart控件
        private void dat_file_convert_to_data(string file_name, System.Windows.Forms.DataVisualization.Charting.Chart dataChart)
        {
            StreamReader read_file = new StreamReader(file_name);
            try
            {
                Int32 line_length = 0;
                string line_context;

                software_status.data_value = new DataTable();
                software_status.data_value.Columns.Add("Times", typeof(float));
                software_status.data_value.Columns.Add("Value", typeof(float));
                DataRow row_value ; 
                while ((!read_file.EndOfStream) && (line_length < MAX_SHOW_LENGTH))
                {
                    line_context = read_file.ReadLine();
                    row_value = software_status.data_value.NewRow();
                    row_value["Times"] = float.Parse(line_context.Substring(0, line_context.IndexOf(" ")));                 
                    row_value["Value"] = float.Parse(line_context.Substring(line_context.IndexOf(" ")+1, line_context.Length - line_context.IndexOf(" ") - 1));
                    software_status.data_value.Rows.Add(row_value);
                    Waveform.Series[0].Points.AddXY(row_value["Times"], row_value["Value"]);
                    line_length++;
                }
                message_info_output(false, message_info.STRING_OUT, "Load DataTable Finished!");
            }
            catch
            {
                MessageBox.Show("File Load Failed!");
            }
            finally
            {
                read_file.Close();
            }
        }

      //richtextbox 控件的Information显示
      //red_flag:输出字体是否为红色,默认为黑色
      //info 数字信息的类型,见上面定义的message_info
      //context 仅对于message info为STRING_OUT有效
        private void message_info_output(bool red_flag,message_info info,string context)
        {
            if (red_flag)
            {
                MessageInfo.SelectionColor = Color.Red;
            }
            switch (info)
            {
                case message_info.FORMAT_ERROR:
                    break;
                case message_info.STRING_OUT:
                    MessageInfo.AppendText(context+"\r\n");
                    break;
                default:
                    break;
            }
            MessageInfo.SelectionColor = Color.Black;
            MessageInfo.ScrollToCaret();
        }
    }
}

当然了,这个上位机处理软件还是可以优化的,比如,Chart的显示,如果数据点数超过10万个点,数据加载会变慢,非常影响用户体验。这些问题,我准备找时间来优化,到时候分享出来供大家参考。

原文链接:,转发请注明来源!