C# modbus 图表

时间:2024-07-17 09:18:19

控件:chart1(图表),cartesianChart1(第三方添加图表),timer(时间)

添加第三方:

效果:图标会根据连接的温度,湿度用timer时间进行改变

Chart1控件样式:Series添加线条,颜色,名称等数据

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        //1 安装livecharts第三方; 安装livechartswinform第三方;
        timer1.Interval = 1000;
        timer1.Start();

        //给图表添加系列
        //new LineSeries() 添加一个线性的系列
        // values 指的是线性系列图表值是 ChartValues类型当中的float类型
       
        cartesianChart1.Series.Add(new LineSeries() { Values = new ChartValues<double>() }); //温度
        cartesianChart1.Series.Add(new LineSeries() { Values = new ChartValues<double>() });//湿度


        
        //给x轴加时间
       cartesianChart1.AxisX.Add(new LiveCharts.Wpf.Axis() { Labels = times });


       //创建客户端对象
        tcp = new TcpClient();//创建客户端对象
        tcp.Connect("192.168.107.5", 8014);
        stream = tcp.GetStream(); //获取网络基础流

    }
    TcpClient tcp;
    NetworkStream stream;


    public List<string> times = new List<string>();
    Random r = new Random();// 随机数对象
    double 温度;
    double 湿度;
    //实时更新数据
    private void timer1_Tick(object sender, EventArgs e)
    {


        // 模拟数据
        //float 温度 = r.Next(-100, 410) / 10F;//模拟请求 请求温度数据
        //float 湿度 = r.Next(-100, 410) / 10F; //读取湿度
        refresh();


        //给chart1 添加数据
        // 给系列1添加xy轴 ,x轴是时间,y轴是温度数据
        chart1.Series[0].Points.AddXY(DateTime.Now.ToString("HH:mm:ss"), 温度);
        chart1.Series[1].Points.AddXY(DateTime.Now.ToString("HH:mm:ss"), 湿度);

        //超出一定数值之后 删除一部分图表?
        if (chart1.Series[0].Points.Count>=10)
        {
            chart1.Series[0].Points.RemoveAt(0);
            chart1.Series[1].Points.RemoveAt(0);
            
        }
        // 给cartesianChart1添加数据
        times.Add(DateTime.Now.ToString());
        cartesianChart1.Series[0].Values.Add(温度);
        cartesianChart1.Series[1].Values.Add(湿度);
    }

    async void refresh()
    {
        try
        {

            //请求帧
            byte[] buffer = null;
            //响应帧
            byte[] res = null;
            //设备地址
            byte address = Convert.ToByte("1");
            //响应数据的个数
            int resCount = -1;
            //响应帧的crc
            byte[] crc;
            buffer = new byte[] { address, 0x03, 0x00, 0x02, 0x00, 0x02 };
            buffer = buffer.Concat(CRCCalc(buffer)).ToArray();

            //异步发请求。发送请求帧
            await stream.WriteAsync(buffer, 0, buffer.Length);

            //接收数据 响应帧。[地址码 功能码 有效个数,0x00,0x01,0x00,0x01,高位 地位]
            res = new byte[9];
            resCount = await stream.ReadAsync(res, 0, res.Length);

            //数据验证

            // 数据验证
            if (resCount == 0 || resCount != res.Length)
            {
                Console.WriteLine("读取湿度、温度时,数据长度不正确");
                return;
            }
            // 验证地址码和功能码
            if (res[0] != buffer[0] || res[1] != buffer[1])
            {
                Console.WriteLine("读取湿度、温度时,设备地址或功能码不匹配");
                return;
            }
            // 验证CRC
            crc = CRCCalc(res, 0, res.Length - 2);
            if (crc[0] != res[res.Length - 2] || crc[1] != res[res.Length - 1])
            {
                Console.WriteLine("读取湿度、温度时,CRC验证失败");
                return;
            }

            //数据解析 读取数据
            double humidity = (res[3] * 256 + res[4]) * 0.1; //湿度
            double wendu = (res[5] * 256 + res[6]) * 0.1;
            湿度 = humidity;
            温度 = wendu;


        }
        catch
        {
            MessageBox.Show("获取数据失败");
        }

    }
    public static byte[] CRCCalc(byte[] data)
    {
        //crc计算赋初始值
        int crc = 0xffff;
        for (int i = 0; i < data.Length; i++)
        {
            //XOR
            //(1) 0^0=0,0^1=1  0异或任何数=任何数
            //(2) 1 ^ 0 = 1,1 ^ 1 = 0  1异或任何数-任何数取反
            //(3) 1 ^ 1 = 0,0 ^ 0 = 0  任何数异或自己=把自己置0
            //异或操作符是^。异或的特点是相同为false,不同为true。
            crc = crc ^ data[i]; //和^表示按位异或运算。
                                 //0x0fff ^ 0x01 Console.WriteLine(result.ToString("X")); 
                                 // 输出结果为4094,即十六进制数1001
            for (int j = 0; j < 8; j++)
            {
                int temp;
                temp = crc & 1; // & 运算符(与) 1 & 0 为 0  ;0 & 0 为0;1 & 1 为1

                //右移 (>>) 将第一个操作数向右移动第二个操作数所指定的位数,空出的位置补0。右移相当于整除. 右移一位相当于除以2;右移两位相当于除以4;右移三位相当于除以8。
                //int i = 7;
                //int j = 2;
                //Console.WriteLine(i >> j);   //输出结果为1
                crc = crc >> 1;
                crc = crc & 0x7fff;
                if (temp == 1)
                {
                    crc = crc ^ 0xa001;
                }
                crc = crc & 0xffff;
            }
        }
        //CRC寄存器的高低位进行互换
        byte[] crc16 = new byte[2];
        //CRC寄存器的高8位变成低8位,
        crc16[1] = (byte)((crc >> 8) & 0xff);
        //CRC寄存器的低8位变成高8位
        crc16[0] = (byte)(crc & 0xff);
        return crc16;
    }
    public static byte[] CRCCalc(byte[] data, int offset, int length)
    {
        byte[] Tdata = data.Skip(offset).Take(length).ToArray();
        return CRCCalc(Tdata);
    }

}