精华内容
下载资源
问答
  • C#开发简单上位机,可以实现传感器数据读取,存储及数据的可视化(通过波形展示),还能实现截图,除此之外还能够改变界面风格(基本颜色的调整,网格调整)
  • 一个用C#开发上位机的帮助类库,能减少重复开发某个模块的工作量。提高工作效率。同时帮助初学者提高自己的业务水平。里面包含了Windows API、加密解密序列化、可移动设备管理类、各种仪表控件库、多媒体处理类、...
  • 基于C#上位机开发实战指南,介绍了上位机软件开发过程中重点内容以及注意事项。
  • c#上位机开发学习

    千次阅读 2020-08-05 10:43:04
    c#上位机开发学习 倒计时器 少年去游荡,中年想掘藏,老年做和尚 所用控件: **timer:**时间控制,此控件被启动后可以按照一定的时间间隔来运行,可以自行设定时间间隔。通过timer.start()或timer.stop()启动或...

    c#上位机开发学习 一

    倒计时器

    少年去游荡,中年想掘藏,老年做和尚

    在这里插入图片描述

    所用控件:

    **timer:**时间控制,此控件被启动后可以按照一定的时间间隔来运行,可以自行设定时间间隔。通过timer.start()或timer.stop()启动或停止。

    comboBox

    progressBar

    小结:

    诸如comboBox,它在窗口显示的数值可以通过comboBox.text = “123123”这样的语句来改变。而progressBar由于是进度条,按照比例显示。通过progressBar.Maximum来设定最大值,然后在它显示的时候进度条则会根据他此时的数值所占最大值的比例的变化而变化。数值更改通过progressBar.value。

    源码链接:https://download.csdn.net/download/qq_41661878/12662241

    串口控制

    const基本用法:

    1、 const定义常变量后,该变量没有写权限,只有读权限 。个人理解是用const定义一个数值后便不能再被更改。

    ①const用于定义常变量时,要进行初始化

    例如:

    const int a=10; //合法

    而const int a; //非法

    ②数据类型对于const而言是透明的

    例如:

    const int a=10; 等价于 int const a=10;

    const int *p1=&a;等价于int const *p1=&a; 但不等价于int *const p1=&a;

    const原文链接:https://blog.csdn.net/qq_40874221/article/details/79855317

    2、serialPort1.Write(buffer, 0, 1);其中buffer为发送的数组,第二个代表发送从哪里开始,第三个是发送几个字节。

    打开/关闭串口代码:

    private void button1_Click(object sender, EventArgs e)
            {
                if(serialPort1.IsOPen)
                    serialPort1.Close();
            }catch{
        	button1.Text = "打开串口";
    }else{
        try{
            seriialPort1.PortName = comboBox1.Text;
            serialPPort1.Open();
            button1.Text = "关闭";
        }
        catch{
            MessageBox.Show("串口打开失败","错误");
        }
    }
    

    发送数据:

    private void WriteByteToSerialPort(byte data)                   //单字节写入串口
            {
                byte[] Buffer = new byte [2]{0x00, data };                //定义数组
                if (serialPort1.IsOpen)                      //传输数据的前提是端口已打开
                {
                    try
                    {
                        serialPort1.Write(Buffer, 0, 2);                    //写数据
                    }
                    catch 
                    {
                        MessageBox.Show("串口数据发送出错,请检查.","错误");//错误处理
                    }
                }
            }
    
    

    串口控制:

    private void SearchAndAddSerialToComboBox(SerialPort MyPort, ComboBox MyBox)//serialport目的是传入serialport1,ComboBox MyBox传入下拉框
            {        									 //将可用端口号添加到ComboBox
               
                string Buffer;                            //缓存
                MyBox.Items.Clear();                                        //清空ComboBox内容
                for (int i = 1; i < 20; i++)                                //循环
                {
                    try                                                     //核心原理是依靠try和catch完成遍历
                    {
                        Buffer = "COM" + i.ToString();
                        MyPort.PortName = Buffer;
                        MyPort.Open();                                      //如果失败,后面的代码不会执行
    
                        MyBox.Items.Add(Buffer);                            //打开成功,添加至下俩列表
                        MyPort.Close();                                     //关闭
    
                    }
                    catch 
                    {
    
                    }
                }
            }
    
    

    先就这么多吧,成品还没整理。回头放

    更新:
    上位机读取GPS数据根据经纬度显示在地图上
    https://blog.csdn.net/qq_41661878/article/details/107821426

    展开全文
  • PLC 或相关的电气工程师量身定制的内容.pdf
  • c#上位机开发(三)——串口通信上位机开发1

    千次阅读 多人点赞 2019-08-21 16:57:43
    今天主要做一个跟市面上差不多的稍微简单点的上位机软件,效果如下图所示 1.功能概述 (1)端口扫描,主要是扫描出可用的端口用来连接 (2)波特率的选择,使用一个下拉框进行选择相应的波特率数值 (3)发送...

    今天主要做一个跟市面上差不多的稍微简单点的上位机软件,效果如下图所示

    1.功能概述

    (1)端口扫描,主要是扫描出可用的端口用来连接

    (2)波特率的选择,使用一个下拉框进行选择相应的波特率数值

    (3)发送模式以及接受模式的选择,目前有2种可供选择,数值模式以及字符串模式

    (4)串口打开以及串口关闭的操作,利用一个按钮来实现

    (5)接收数据的显示

    (6)发送数据

    2.功能实现

    (1)端口扫描

    主要是使用try的手法去测试每一个端口,直接打开端口,如果打不开就是扫描失败的端口,则不能使用,具体代码见下

    private void scanport(SerialPort MyPort, ComboBox MyBox)
            {
                string[] Mystring = new string[20];//用来存放端口的名称
                string buffer;//用来存放端口名
                MyBox.Items.Clear();//清空下拉框
                int count = 0;//用来作为数组的下标
                for (int i = 1; i < 20; i++)//循坏测试可用的端口号
                {
                    try
                    {
                        buffer = "COM" + i.ToString();//存放端口的名称
                        MyPort.PortName = buffer;//设置端口的名称为buffer
                        MyPort.Open();//打开端口名为buffer变量的串口,如果失败则不调用下面的函数直接到catch
                        Mystring[count] = buffer;//将可用端口名称存放在字符串数组中
                        MyBox.Items.Add(buffer);//在下拉框中添加可用的端口名
                        MyPort.Close();//关闭端口
                        count++;
                    }
                    catch 
                    {
                    }
                }
                MyBox.Text = Mystring[0];
            }

    (2)波特率的选择

    需要在波特率下拉框中设定好元素,具体看下图,点击下拉框,在属性中找到items,点击进入手动写入可选择的波特率数值

    (3)打开串口或者关闭串口按钮操作

    private void button1_Click(object sender, EventArgs e)
            {
                if (serialPort1.IsOpen)
                {
                    try
                    {
                        serialPort1.Close();
                    }
                    catch { }
                    button1.Text = "打开串口";
                }
    
                else
                {
                    try
                    {
                        serialPort1.PortName = comboBox1.Text;//端口号修改
                        serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//十进制数据转换
                        serialPort1.Open();//打开串口
                    }
                    catch
                    {
                        MessageBox.Show("接口错误,请检查串口", "错误");
                    }
                    button1.Text = "关闭串口";
                }
                
            }

    (4)接收数据

    private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) //串口数据接收事件
            {
                if (!radioButton3.Checked)//如果接收模式为字符模式
                {
                    String str = serialPort1.ReadExisting();//字符串模式读
                    textBox1.AppendText(str);//在文本框框中添加内容
                }
                else//如果接收模式为数值模式
                {
                    byte data;
                    data = (byte)serialPort1.ReadByte();//强制类型转换,将整形转化为byte类型数据
                    string str = Convert.ToString(data, 16).ToUpper();//转换为大写十六进制字符串
                    textBox1.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");//空位补0
                }
            }

    (5)发送数据

    private void button3_Click(object sender, EventArgs e)
            {
                byte[] Data = new byte[1];//作用同上集
                if (serialPort1.IsOpen)//判断串口是否打开,如果打开执行下一步操作
                {
                    if (textBox2.Text != "")
                    {
                        if (!radioButton1.Checked)//如果发送模式是字符模式
                        {
                            try
                            {
                                serialPort1.WriteLine(textBox2.Text);//写数据
                            }
                            catch (Exception err)
                            {
                                MessageBox.Show("串口数据写入错误", "错误");//出错提示
                                serialPort1.Close();
                                button1.Enabled = true;//打开串口按钮可用
                                button2.Enabled = false;//关闭串口按钮不可用
                            }
                        }
                        else
                        {
                            for (int i = 0; i < (textBox2.Text.Length - textBox2.Text.Length % 2) / 2; i++)//取余3运算作用是防止用户输入的字符为奇数个
                            {
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(i * 2, 2), 16);
                                serialPort1.Write(Data, 0, 1);//循环发送(如果输入字符为0A0BB,则只发送0A,0B)
                            }
                            if (textBox2.Text.Length % 2 != 0)//剩下一位单独处理
                            {
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);//单独发送B(0B)
                                serialPort1.Write(Data, 0, 1);//发送
                            }
                        }
                    }
                }
            }

     

     

     

     

    附一下后端代码

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.IO.Ports;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace SerialPort2
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
            }
            private void Form1_Load(object sender, EventArgs e)
            {
                scanport(serialPort1, comboBox1);
                //comboBox1.Text = "COM1";//串口号默认值
                comboBox2.Text = "9600";//串口默认波特率
                serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);//手动添加事件处理程序
            }
            private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) //串口数据接收事件
            {
                if (!radioButton3.Checked)//如果接收模式为字符模式
                {
                    String str = serialPort1.ReadExisting();//字符串模式读
                    textBox1.AppendText(str);//在文本框框中添加内容
                }
                else//如果接收模式为数值模式
                {
                    byte data;
                    data = (byte)serialPort1.ReadByte();//强制类型转换,将整形转化为byte类型数据
                    string str = Convert.ToString(data, 16).ToUpper();//转换为大写十六进制字符串
                    textBox1.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");//空位补0
                }
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                if (serialPort1.IsOpen)
                {
                    try
                    {
                        serialPort1.Close();
                    }
                    catch { }
                    button1.Text = "打开串口";
                }
    
                else
                {
                    try
                    {
                        serialPort1.PortName = comboBox1.Text;//端口号修改
                        serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//十进制数据转换
                        serialPort1.Open();//打开串口
                    }
                    catch
                    {
                        MessageBox.Show("接口错误,请检查串口", "错误");
                    }
                    button1.Text = "关闭串口";
                }
                
            }
    
            private void button3_Click(object sender, EventArgs e)
            {
                byte[] Data = new byte[1];//作用同上集
                if (serialPort1.IsOpen)//判断串口是否打开,如果打开执行下一步操作
                {
                    if (textBox2.Text != "")
                    {
                        if (!radioButton1.Checked)//如果发送模式是字符模式
                        {
                            try
                            {
                                serialPort1.WriteLine(textBox2.Text);//写数据
                            }
                            catch (Exception err)
                            {
                                MessageBox.Show("串口数据写入错误", "错误");//出错提示
                                serialPort1.Close();
                                button1.Enabled = true;//打开串口按钮可用
                                button2.Enabled = false;//关闭串口按钮不可用
                            }
                        }
                        else
                        {
                            for (int i = 0; i < (textBox2.Text.Length - textBox2.Text.Length % 2) / 2; i++)//取余3运算作用是防止用户输入的字符为奇数个
                            {
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(i * 2, 2), 16);
                                serialPort1.Write(Data, 0, 1);//循环发送(如果输入字符为0A0BB,则只发送0A,0B)
                            }
                            if (textBox2.Text.Length % 2 != 0)//剩下一位单独处理
                            {
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);//单独发送B(0B)
                                serialPort1.Write(Data, 0, 1);//发送
                            }
                        }
                    }
                }
            }
    
            private void scanport(SerialPort MyPort, ComboBox MyBox)
            {
                string[] Mystring = new string[20];//用来存放端口的名称
                string buffer;//用来存放端口名
                MyBox.Items.Clear();//清空下拉框
                int count = 0;//用来作为数组的下标
                for (int i = 1; i < 20; i++)//循坏测试可用的端口号
                {
                    try
                    {
                        buffer = "COM" + i.ToString();//存放端口的名称
                        MyPort.PortName = buffer;//设置端口的名称为buffer
                        MyPort.Open();//打开端口名为buffer变量的串口,如果失败则不调用下面的函数直接到catch
                        Mystring[count] = buffer;//将可用端口名称存放在字符串数组中
                        MyBox.Items.Add(buffer);//在下拉框中添加可用的端口名
                        MyPort.Close();//关闭端口
                        count++;
                    }
                    catch 
                    {
                    }
                }
                MyBox.Text = Mystring[0];
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                scanport(serialPort1, comboBox1);
            }
            
        }
    }
    

     

    还有就是一些问题讲一下

    (1)单选框分开控制

    可是使用panel组件把需要一块处理的单选框或者多选框放在一个panel组件中,这样就不会所有的单选或者多选框会影响了

     

    附一下代码下载链接

    serialport2

     

    展开全文
  • c#上位机开发总结

    千次阅读 2020-06-29 16:51:58
    对了,还没有说当上位机接收到这个结构体之后如何解析呢,怎么解析,c#也是支持结构体的,所以我把这个结构拷贝一份,拿到c#中,再写个字节数组转结构体的方法,获取到对应字段的值,如果不用这种方法的话,智只能...

    1.窗体间传值

    • 委托
    		//声明委托
            public delegate void BtnClickDelagate(object sender, EventArgs e);
    		//创建委托事件
            public event BtnClickDelagate UserControlClick;
    
    • 全局变量
     		//创建变量实例
            public Variable_Modbus var;
            //创建报警变量实例
            public VarAlarm_Modbus varAlarm;
    

    2.在类库中创建文件想放到文件夹中,不要直接在文件夹中创建,应该在类库中创建,然后拖入到文件夹里,不然命名空间会报错

    3.引用新的类库后,仍然报错找不到,此时应该关闭项目在重新打开

    4.当使用dateGradView的时候,表格内容如果需要从下位机获取,那么该如何设计呢?

    我之前的做法:
    1.下位机是c语言实现,并没有类的概念,所以使用结构体来实现一条记录。但是在填充表格控件的时候没法设置数据源,只好我手动添加一行,添加一列,写个for循环,如果结构体还有嵌套的话,那么在fort循环里还要继续for,如果你的表格结构字段又添加一个或者删除一个,之前的工作还有再重新做一遍,非常麻烦!!!对了,还没有说当上位机接收到这个结构体之后如何解析呢,怎么解析,c#也是支持结构体的,所以我把这个结构拷贝一份,拿到c#中,再写个字节数组转结构体的方法,获取到对应字段的值,如果不用这种方法的话,智只能通过数组下标来解析结构体中各个字段的值了,很头疼吧。。。

    实现的效果
    新的方法:
    上位机的表格控件绑定数据源,数据源可以是一个集合。

    this.dgv_data.DataSource = this.VarList;
    
    1. 如何获取list ?
      这个集合是从单片机获取的,那么单片机的数据结构里没有集合,只有结构体
      结构体 —> 集合
      集合也没有协议规定啊,怎么转换呢?所以需要一个协议或者格式把结构体转换一下
      结构体 —> json/xml —> 集合
    2. 单片机端何如使用 json/xml
      xml的解析可以使用mxml(开源xml解析器)
      json解析可以使用cjson,我感觉json更简单,所以我选择json
    3. 单片机发送json到上位机
      单片机把结构体数组转换为json字符串(json中也是有数组概念的)进行传输,传输json文件也可以,因为这个是需要保存的,系统上电的时候把这个文件读到ram里面,然后应用程序需要使用里面的数据。话说字符串传输和文件传输也没啥区别。。。
    4. 上位机将json显示到表格中
      上位机收到数据之后,遍历一下,取出数据保存到实体类中,然后依次加入到集合,最后设置表格的DataSource属性,完成。
      c#解析json用到Newtonsoft,基本使用方法学习https://blog.csdn.net/u013667796/article/details/83826592
      代码:
    		public static List<Variable_Sensor> LoadSensorJSON(string jsonpath)
            {
                List<Variable_Sensor> VarSensorList = null;
                if (!File.Exists(jsonpath))
                {
                    MessageBox.Show("IO配置变量的json文件不存在!");
                }
                else
                {
                    VarSensorList = new List<Variable_Sensor>();
    
                    StreamReader file = File.OpenText(jsonpath);
                    JsonTextReader reader = new JsonTextReader(file);
                    JObject jsonObject = (JObject)JToken.ReadFrom(reader);
                    JObject json_root = (JObject)jsonObject["Root"];
                    var json_root_var = json_root["Variable"];
                    foreach (JObject obj in json_root_var)
                    {
                        Variable_Sensor objVar = new Variable_Sensor();
                        objVar.VarName = obj["VarName"].ToString();
                        objVar.Address = obj["Address"].ToString();
                        objVar.DataType = obj["DataType"].ToString();
                        objVar.StoreArea = obj["StoreArea"].ToString();
                        objVar.Note = obj["Note"].ToString();
                        objVar.RegAdd = obj["RegAdd"].ToString();
                        objVar.RegAddLen = obj["RegAddLen"].ToString();
                        objVar.Unit = obj["Unit"].ToString();
                        objVar.IsUse = obj["IsUse"].ToString();
                        VarSensorList.Add(objVar);
                    }
                    reader.Close();
                    file.Close();
                }
                return VarSensorList;
            }
    

    效果图:
    在这里插入图片描述
    在这里插入图片描述
    下位机结构体与json互转的方法:
    struct.h

    #ifndef __TAB_STRUCT_
    #define __TAB_STRUCT_
    
    #include "data_type.h"
    #include "cJSON.h"
    
    #define LINKAGE_COUNT 8//联动输出数量
    
    
    #pragma pack (1)
    #pragma pack ()
    
    
    //状态 0或者1
    typedef enum _TAB_STA_ENUM
    {
    	DISABLE_STA,
    	ENABLE_STA,
    }TAB_STA_ENUM;
    
    //接口类型 上位机也有一份 在json文件里
    typedef enum _INTERFACE_ENUM
    {
    	TYPE_4851,
    	TYPE_4852,
    	TYPE_232_1,
    	TYPE_232_2,
    	TYPE_TCP,
    	TYPE_LORA,
    	DI_1,
    	DI_2,
    	DI_3,
    	DI_4,
    	DI_5,
    	DI_6,
    	DI_7,
    	DI_8
    }INTERFACE_ENUM;
    
    /* 数值类型 */
    typedef enum _VALUE_TYPE
    {
    	VALUE_BOOL,
    	VALUE_INT,
    	VALUE_FLOAT,
    }VALUE_TYPE_ENUM;
    
    /* 报警类型 */
    typedef enum _WARNTYPE
    {
    	WARN_DISABLE,//禁用告警 VALUE_BOOL
    	WARN_CLOSE,//闭合告警 VALUE_BOOL
    	WARN_BREAK,//断开告警 VALUE_BOOL
    	WARN_DEST,//定值报警
    	WARN_HI,//高报警 高于设定值
    	WARN_LI,//低报警 低于设定值
    	WARN_HILI//高低报警 范围内
    }WARNTYPE_ENUM;
    
    //协议类型
    typedef enum _AMT_TYPE_ENUM
    {
    	AMT_DI,
    	AMT_MODBUS,
    	AMT_DOOR,
    	AMT_UPS,
    	AMT_SCREEN,
    	AMT_COUNT,
    	AMT_NONE
    }AMT_TYPE_ENUM;
    
    /* 用户表记录的数量 */
    #define USERTAB_RECORD_NUM 50
    #define ROOT_TAB_SENIOR_LEN 20//传感器型号长度
    #define TAB_UARTPARAMS_NUM 12+1//接口参数 "115200,8,1,0" 数据区最大长度+1 +'\0'
    #define TAB_NOTE_NUM 30+1 //设备表 设备描述 安装位置 备注 的长度 中文占2字节 +'\0'
    #define TAB_UNIT_LEN 10+1//单位长度 +'\0'
    #define AMT_TAG_LEN 20+1//单位长度 +'\0'
    #define LINKAGE_OUT_NUM 4//联动输出的数量
    
    #define CACHE_DATA_SIZE 50//缓存表中数据长度
    
    //报警
    typedef struct _WarningTypedef
    {
    	/* 报警方式 */
    	WARNTYPE_ENUM type;
    	/* 报警值类型 根据WARNTYPE_ENUM得到 VALUE_BOOL用不到下面的字段*/
    	VALUE_TYPE_ENUM valtype;				
    	int valueint;				/* The item's number, if type==VALUE_INT */
    	float valuefloat;			/* The item's number, if type==VALUE_FLOAT */
    }WarningTypedef;
    
    //联动
    typedef struct _LinkAgeTypedef
    {
    	TAB_STA_ENUM dout[LINKAGE_OUT_NUM];
    	u8 dout_t[LINKAGE_OUT_NUM];
    }LinkAgeTypedef;
    
    //系数
    typedef struct _VAL_MODULUS
    {
    	VALUE_TYPE_ENUM type;				
    	int valueint;
    	float valuefloat;
    }ValModulusTypedef;
    
    //协议
    typedef struct _AmtParamsTypedef
    {
    	/* 协议类型 */
    	AMT_TYPE_ENUM type;
    	/* 标识符 */
    	char tag[AMT_TAG_LEN];
    
    	/* 下面只针对modbus协议 */
    	/* 功能码 */
    	u8 fun;
    	/* 寄存器地址 */
    	uint16_t reg_addr;
    	/* 寄存器长度 */
    	uint16_t reg_len;
    	/* 单位 */
    	char unit[TAB_UNIT_LEN];
    	/* 系数 */
    	ValModulusTypedef modulus;
    }AmtParamsTypedef;
    
    //用户记录格式
    typedef struct _UserRecordTypedef
    {
    	/* 记录的id 自增长 1++ */
    	u16 id;
    	/* 是否启用 */
    	TAB_STA_ENUM isuse;
    	/* 接口 */
      	INTERFACE_ENUM interfac;
    	/* 通信ID */
    	u16 commid;
    	/* 串口参数 */
    	char uart_params[TAB_UARTPARAMS_NUM];
    	/* 超时时间ms */
    	u16 timeout;
    	/* 传感器型号 和管理员表中的model相同 */
    	char model[ROOT_TAB_SENIOR_LEN];
    	/* 备注 和管理员表中的note相同 */
    	char note[TAB_NOTE_NUM];
    	/* 告警参数 */
    	WarningTypedef warning_params;
    	/* 联动输出 */
    	LinkAgeTypedef linkage_params;
    	/* 协议参数 */
    	AmtParamsTypedef amt_params;
    
    }UserRecordTypedef;
    
    #define Size_UserRecordTypedef sizeof(UserRecordTypedef)
    	
    //用户配置表
    typedef struct _UserTab{
    	/* 有效记录的数量 */
    	u8 num;
    	UserRecordTypedef array[USERTAB_RECORD_NUM];
    }UserTabTypedef;
    
    #define Size_UserTabTypedef sizeof(UserTabTypedef)
    
    extern UserTabTypedef userTab;	
    
    //缓存表
    typedef struct _CacheRecord{
    	/* 数据区长度 */
    	u16 data_len;
    	/* 数据区 */
    	u8 data[CACHE_DATA_SIZE];
    	int valueint;
    	float valuefloat;
    	/* 错误计数 */
    	u8 err_num;
    	/* 在线状态 */
    	u8 isonline;
    	/* 报警状态 */
    	u8 iswarning;
    }CacheRecordTypedef;
    
    #define Size_CacheRecordTypedef sizeof(CacheRecordTypedef)
    
    extern CacheRecordTypedef cacheTab[USERTAB_RECORD_NUM];
    
    void save_user_tab(void);
    void user_tab_init(void);
    char *userTabToJsonString(u8 fmt);
    char *cacheTabToJsonString(u8 fmt);
    char *userTabStructToJson();
    u8 JsonStringToUserTab(char* p);
    cJSON *parseRootJson(cJSON *pJson);
    cJSON *parseVarJson(cJSON * pJson);
    
    u8 parse_uartConfig_string(u8* string,u8 stringLen,u32* bps,u8* dataLen,u8* stopLen,u8* parity);
    
    #endif
    
    
    

    struct.c

    /*
     * @Descripttion: 
     * @version: 1.0.0
     * @Author: zhangyasheng
     * @Date: 2020-07-05 11:13:14
     * @LastEditors: zhangyasheng
     * @LastEditTime: 2020-07-08 12:40:12
     */ 
    #include "tab_struct.h"
    #include "w25qxx.h"
    #include "led.h"
    #include "string.h"
    #include "malloc.h"
    #include "common.h"
    #include "some_sensor.h"
    
    /********************************************************************/
    //1.json的键名 首字母大写 (c#构造方法中 首字母是大写的)
    /********************************************************************/
    
    UserTabTypedef userTab;
    CacheRecordTypedef cacheTab[USERTAB_RECORD_NUM];
    cJSON *makeUserRecordJson(u16 i);
    cJSON *makeCacheRecordJson();
    cJSON *makeVarArrayJson();
    cJSON *makeVarJson();
    cJSON *makeRootJson();
    char *userTabToJsonString(u8 fmt);
    cJSON *parseVarArrayJson(cJSON * pJson);
    cJSON *parseUserRecordJson(cJSON *pJson,u16 i);
    
    void user_tab_init()
    {
    	OS_CPU_SR cpu_sr = 0;
    	char *p;
    		
    	OS_ENTER_CRITICAL();
    	W25QXX_Read((u8*)&userTab,USERCONFIGTAB_ADDR,USERCONFIGTAB_SIZE);
    	OS_EXIT_CRITICAL();
    	printf("ram:%d",my_mem_perused(SRAMIN));
    //	userTab.num = 1;
    //	userTab.array[0].id = 1;
    //	userTab.array[0].isuse = ENABLE_STA;
    //	userTab.array[0].interfac = TYPE_4852;
    //	userTab.array[0].commid = 10;
    //	sprintf(userTab.array[0].uart_params,"9600,8,1,0");
    //	sprintf(userTab.array[0].model,"123");
    //	sprintf(userTab.array[0].note,"中文");
    //	userTab.array[0].warning_params.type = WARN_DISABLE;
    //	userTab.array[0].warning_params.valtype = VALUE_INT;
    //	userTab.array[0].warning_params.valueint = -12;
    //	userTab.array[0].linkage_params.dout[1] = 1;
    //	userTab.array[0].linkage_params.dout_t[1] = 2;
    //	userTab.array[0].amt_params.type = AMT_MODBUS;
    //	sprintf(userTab.array[0].amt_params.tag,"1#温度");
    //	userTab.array[0].amt_params.fun = 0x03;
    //	userTab.array[0].amt_params.reg_addr = 0x0001;
    //	userTab.array[0].amt_params.reg_len = 1;
    //	sprintf(userTab.array[0].amt_params.unit,"℃");
    //	userTab.array[0].amt_params.modulus.type = VALUE_FLOAT;
    //	userTab.array[0].amt_params.modulus.valuefloat = 0.1;
    //	//p = userTabStructToJson();
    //	p = userTabToJsonString(1);
    //	if(p != NULL)
    //	{
    //		printf("%s",p);
    //	}
    //	printf("\r\n ram:%d",my_mem_perused(SRAMIN));
    //	myfree(SRAMIN,p);
    //	printf("\r\n ram:%d",my_mem_perused(SRAMIN));
    }
    /**
     * @name: 用户表转换为json字符串 记得释放返回值
     * @param 0:不带格式 1:带格式
     * @return: 字符串指针
     */
    char *userTabToJsonString(u8 fmt)
    {
    	char *p;
    	cJSON *pJson=NULL;
    	pJson = makeRootJson();
    
    	if(!fmt)p = cJSON_PrintUnformatted(pJson);//打印调试的时候 可以选这个 不带格式
    	else p = cJSON_Print(pJson);//往文件输出的时候 选择这个 带格式
    
    	cJSON_Delete(pJson);
    	return p;
    }
    /**
     * @name: 缓存表转换为json字符串 记得释放返回值
     * @param 0:不带格式 1:带格式
     * @return: 字符串指针
     */
    char *cacheTabToJsonString(u8 fmt)
    {
    	char *p;
    	cJSON *pJson=NULL;
    	
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	cJSON_AddItemToObject(pJson,"Records",makeCacheRecordJson());
    	cJSON_AddNumberToObject(pJson,"Raminuse",my_mem_perused(SRAMIN));
    	cJSON_AddNumberToObject(pJson,"Ramexuse",my_mem_perused(SRAMEX));
    	cJSON_AddBoolToObject(pJson,"Linken",glo.linkage_en);
    	cJSON_AddBoolToObject(pJson,"Out1",OUT1);
    	cJSON_AddBoolToObject(pJson,"Out2",OUT2);
    	cJSON_AddBoolToObject(pJson,"Out3",OUT3);
    	cJSON_AddBoolToObject(pJson,"Out4",OUT4);
    	cJSON_AddBoolToObject(pJson,"Input1",INPUT1);
    	cJSON_AddBoolToObject(pJson,"Input2",INPUT2);
    	cJSON_AddBoolToObject(pJson,"Input3",INPUT3);
    	cJSON_AddBoolToObject(pJson,"Input4",INPUT4);
    	cJSON_AddBoolToObject(pJson,"Input5",INPUT5);
    	cJSON_AddBoolToObject(pJson,"Input6",INPUT6);
    	cJSON_AddBoolToObject(pJson,"Input7",INPUT7);
    	cJSON_AddBoolToObject(pJson,"Input8",INPUT8);
    
    	if(!fmt)p = cJSON_PrintUnformatted(pJson);//打印调试的时候 可以选这个 不带格式
    	else p = cJSON_Print(pJson);//往文件输出的时候 选择这个 带格式
    
    	cJSON_Delete(pJson);
    	return p;
    }
    /**
     * @name: json字符串转为用户表
     * @param json字符串
     * @return: 0:成功 其他:失败
     */
    u8 JsonStringToUserTab(char* p)
    {
    	cJSON *pJson=NULL;
    	cJSON *pSub=NULL;
    	
    	if(NULL == p)
    	{
    		return 1;
    	}
    
    	pJson = cJSON_Parse(p);
    	if(NULL == pJson)                                                                                         
    	{
    		return 1;
    	}
    
    	pSub = parseRootJson(pJson);
    
    	cJSON_Delete(pJson);
    
    	if(NULL == pSub)                                                                                         
    	{
    		return 1;
    	}
    	
    	return 0;
    }
    /**
     * @name: 创建Root节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeRootJson()
    {
    	cJSON * pJson = NULL;
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	cJSON_AddItemToObject(pJson,"Root",makeVarJson());
    	return pJson;
    }
    /**
     * @name: 解析Root json 并返回json对象
     * @param {type} 
     * @return: cJSON
     */
    cJSON *parseRootJson(cJSON *pJson)
    {
    	cJSON * pSub=NULL;	
    	pSub = cJSON_GetObjectItem(pJson,"Root");
        if(NULL == pSub)
        {
    		return NULL;
        }
    	pSub = parseVarJson(pSub);
    #if 0
    	p = cJSON_Print(pJson);
    	printf("%s",p);
    	myfree(SRAMIN,p);
    #endif
        return pSub;
    }
    
    /**
     * @name: 创建Variable节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeVarJson()
    {
    	cJSON * pJson = NULL;
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	cJSON_AddItemToObject(pJson,"Variable",makeVarArrayJson());
    	return pJson;
    }
    /**
     * @name: 解析Variable节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *parseVarJson(cJSON *pJson)
    {
    	cJSON *pSub=NULL;	
    	char *p=NULL;
        
    	pSub = cJSON_GetObjectItem(pJson, "Variable");
        if(NULL == pSub)
        {
    		return NULL;
        }
    	
    #if 0
    	p = cJSON_Print(pJson);
    	printf("%s",p);
    	myfree(SRAMIN,p);
    #endif
    
    	pSub = parseVarArrayJson(pSub);
    	if(NULL == pSub)
        {
    		return NULL;
        }
    	
        return pSub;
    }
    
    /**
     * @name: 创建VarArray节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeVarArrayJson()
    {
    	u16 i;
    	cJSON * pJson = NULL;
    	pJson = cJSON_CreateArray();
    	if(NULL == pJson)return NULL;
    	for (i = 0; i < userTab.num; i++)
    	{
    		cJSON_AddItemToArray(pJson,makeUserRecordJson(i));
    	}
    	return pJson;
    }
    
    /**
     * @name: 解析VarArray节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *parseVarArrayJson(cJSON * pJson)
    {
    	u16 i=0;
    	u16 iSize=0;
    	u16 iValue=0;	
    	cJSON *pSub=NULL;
    	iSize = cJSON_GetArraySize(pJson);
        for(i = 0; i < iSize; i++)
        {
            pSub = cJSON_GetArrayItem(pJson, i);
            if(NULL == pSub)
            {
                continue;
            }
    		pSub = parseUserRecordJson(pSub,i);
    		if(NULL == pSub)
            {
                return NULL;
            }
        }
    	return pSub;
    }
    
    /**
     * @name: 创建WarningTypedef节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeWarningTypedefJson(u16 i)
    {
    	cJSON * pJson = NULL;
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	/* 告警参数 */
    	cJSON_AddNumberToObject(pJson,"Type",userTab.array[i].warning_params.type);
    	cJSON_AddNumberToObject(pJson,"Valtype",userTab.array[i].warning_params.valtype);
    	if(userTab.array[i].warning_params.valtype == VALUE_BOOL || userTab.array[i].warning_params.valtype == VALUE_INT)
    	{
    		cJSON_AddNumberToObject(pJson,"Valueint",userTab.array[i].warning_params.valueint);
    	}
    	else if(userTab.array[i].warning_params.valtype == VALUE_FLOAT)
    	{
    		cJSON_AddNumberToObject(pJson,"Valuefloat",userTab.array[i].warning_params.valuefloat);
    	}
    	return pJson;
    }
    /**
     * @name: 解析WarningTypedef节点 在array循环中调用
     * @param Warning_params的json结构 记录的下标 
     * @return: 返回参数pjson:成功 NULL: 失败
     */
    cJSON *parseWarningTypedefJson(cJSON *pJson,u16 i)
    {
    	cJSON * pSub = NULL;
    	cJSON * item = NULL;
    
    	pSub = cJSON_GetObjectItem(pJson, "Warning_params");
        if(NULL == pSub)
        {
    		return NULL; 
        }
    
    	item = cJSON_GetObjectItem(pSub,"Type");
    	if(NULL == item)return NULL;
    	userTab.array[i].warning_params.type = item->valueint;
    
    	item = cJSON_GetObjectItem(pSub,"Valtype");
    	if(NULL == item)return NULL;
    	userTab.array[i].warning_params.valtype = item->valueint;
    
    	if(userTab.array[i].warning_params.valtype == VALUE_BOOL || userTab.array[i].warning_params.valtype == VALUE_INT)
    	{
    		item = cJSON_GetObjectItem(pSub,"Valueint");
    		if(NULL == item)return NULL;
    		userTab.array[i].warning_params.valueint = item->valueint;
    	}
    	else if(userTab.array[i].warning_params.valtype == VALUE_FLOAT)
    	{
    		item = cJSON_GetObjectItem(pSub,"Valuefloat");
    		if(NULL == item)return NULL;
    		userTab.array[i].warning_params.valuefloat = item->valuedouble;
    	}
    	
    	return pJson;
    }
    
    
    /**
     * @name: 创建LinkAgeTypedef节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeLinkAgeTypedefJson(u16 i)
    {
    	u8 cnt=0;
    	char *buff1[] = {"Do0","Do1","Do2","Do3"};
    	char *buff2[] = {"Dt0","Dt1","Dt2","Dt3"};
    	cJSON * pJson = NULL;
    
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	/* 联动输出 */
    	for(cnt=0;cnt<LINKAGE_OUT_NUM;cnt++)
    	{
    		cJSON_AddBoolToObject(pJson,buff1[cnt],userTab.array[i].linkage_params.dout[cnt]);
    		cJSON_AddNumberToObject(pJson,buff2[cnt],userTab.array[i].linkage_params.dout_t[cnt]);
    	}
    	
    	return pJson;
    }
    
    /**
     * @name: 解析LinkAgeTypedef节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *parseLinkAgeTypedefJson(cJSON *pJson,u16 i)
    {
    	u8 cnt=0;
    	char *buff1[] = {"Do0","Do1","Do2","Do3"};
    	char *buff2[] = {"Dt0","Dt1","Dt2","Dt3"};
    	cJSON * pSub = NULL;
    	cJSON * item = NULL;
    
    	pSub = cJSON_GetObjectItem(pJson, "Linkage_params");
        if(NULL == pSub)
        {
    		return NULL; 
        }
    
    	/* 联动输出 */
    	for(cnt=0;cnt<LINKAGE_OUT_NUM;cnt++)
    	{
    		item = cJSON_GetObjectItem(pSub,buff1[cnt]);
    		if(NULL == item)return NULL;
    		userTab.array[i].linkage_params.dout[cnt] = item->valueint;
    
    		item = cJSON_GetObjectItem(pSub,buff2[cnt]);
    		if(NULL == item)return NULL;
    		userTab.array[i].linkage_params.dout_t[cnt] = item->valueint;
    	}
    	
    	return pJson;
    }
    
    /**
     * @name: 创建ValModulusTypedef节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeValModulusTypedefJson(u16 i)
    {
    	cJSON * pJson = NULL;
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	/* 系数 */
    	cJSON_AddNumberToObject(pJson, "Type",userTab.array[i].amt_params.modulus.type);
    	if(userTab.array[i].amt_params.modulus.type == VALUE_BOOL || userTab.array[i].amt_params.modulus.type == VALUE_INT)
    	{
    		cJSON_AddNumberToObject(pJson, "Valueint",userTab.array[i].amt_params.modulus.valueint);
    	}
    	else if(userTab.array[i].amt_params.modulus.type == VALUE_FLOAT)
    	{
    		cJSON_AddNumberToObject(pJson, "Valuefloat",userTab.array[i].amt_params.modulus.valuefloat);
    	}
    	return pJson;
    }
    
    /**
     * @name: 解析ValModulusTypedef节点 
     * @param {type} 
     * @return: 返回pJson:成功 NULL:失败
     */
    cJSON *parseValModulusTypedefJson(cJSON *pJson,u16 i)
    {
    	cJSON * pSub = NULL;
    	cJSON * item = NULL;
    
    	pSub = cJSON_GetObjectItem(pJson, "Modulus");
        if(NULL == pSub)
        {
    		return NULL; 
        }
    	
    	item = cJSON_GetObjectItem(pSub,"Type");
    	if(NULL == item)return NULL;
    	userTab.array[i].amt_params.modulus.type = item->valueint;
    	
    	if(userTab.array[i].amt_params.modulus.type == VALUE_BOOL || userTab.array[i].amt_params.modulus.type == VALUE_INT)
    	{
    		item = cJSON_GetObjectItem(pSub,"Valueint");
    		if(NULL == item)return NULL;
    		userTab.array[i].amt_params.modulus.valueint = item->valueint;
    	}
    	else if(userTab.array[i].amt_params.modulus.type == VALUE_FLOAT)
    	{
    		item = cJSON_GetObjectItem(pSub,"Valuefloat");
    		if(NULL == item)return NULL;
    		userTab.array[i].amt_params.modulus.valuefloat = item->valuedouble;
    	}
    	return pJson;
    }
    
    /**
     * @name: 创建AmtParamsTypedef节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeAmtParamsTypedefJson(u16 i)
    {
    	cJSON * pJson = NULL;
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	/* 协议参数 */
    	cJSON_AddNumberToObject(pJson,"Type",userTab.array[i].amt_params.type);
    	userTab.array[i].amt_params.tag[AMT_TAG_LEN-1]=0;
    	cJSON_AddStringToObject(pJson,"Tag",userTab.array[i].amt_params.tag);
    	cJSON_AddNumberToObject(pJson,"Fun",userTab.array[i].amt_params.fun);
    	cJSON_AddNumberToObject(pJson,"Reg_addr",userTab.array[i].amt_params.reg_addr);
    	cJSON_AddNumberToObject(pJson,"Reg_len",userTab.array[i].amt_params.reg_len);
    	userTab.array[i].amt_params.unit[TAB_UNIT_LEN-1]=0;
    	cJSON_AddStringToObject(pJson,"Unit",userTab.array[i].amt_params.unit);
    	cJSON_AddItemToObject(pJson,"Modulus",makeValModulusTypedefJson(i));
    	return pJson;
    }
    
    /**
     * @name: 解析AmtParamsTypedef节点 
     * @param {type} 
     * @return: 返回pJson:成功 NULL:失败
     */
    cJSON *parseAmtParamsTypedefJson(cJSON *pJson,u16 i)
    {
    	cJSON * pSub = NULL;
    	cJSON * item = NULL;
    	u8 len=0;
    
    	pSub = cJSON_GetObjectItem(pJson, "Amt_params");
        if(NULL == pSub)
        {
    		return NULL; 
        }
    	
    	item = cJSON_GetObjectItem(pSub,"Type");
    	if(NULL == item)return NULL;
    	userTab.array[i].amt_params.type = item->valueint;
    
    	item = cJSON_GetObjectItem(pSub,"Tag");
    	if(NULL == item)return NULL;
    	len = strlen(item->valuestring);
    	if(len>AMT_TAG_LEN)	len=AMT_TAG_LEN;
    	memcpy(userTab.array[i].amt_params.tag,item->valuestring,len);
    	userTab.array[i].amt_params.tag[AMT_TAG_LEN-1]=0;
    
    	item = cJSON_GetObjectItem(pSub,"Fun");
    	if(NULL == item)return NULL;
    	userTab.array[i].amt_params.fun = item->valueint;
    
    	item = cJSON_GetObjectItem(pSub,"Reg_addr");
    	if(NULL == item)return NULL;
    	userTab.array[i].amt_params.reg_addr = item->valueint;
    
    	item = cJSON_GetObjectItem(pSub,"Reg_len");
    	if(NULL == item)return NULL;
    	userTab.array[i].amt_params.reg_len = item->valueint;
    	
    	item = cJSON_GetObjectItem(pSub,"Unit");
    	if(NULL == item)return NULL;
    	len = strlen(item->valuestring);
    	if(len>TAB_UNIT_LEN)	len=TAB_UNIT_LEN;
    	memcpy(userTab.array[i].amt_params.unit,item->valuestring,len);
    	userTab.array[i].amt_params.unit[TAB_UNIT_LEN-1]=0;
    
    	item = parseValModulusTypedefJson(pSub,i);
    	if(NULL == item)return NULL;
    	
    	return pJson;
    }
    
    /**
     * @name: 创建UserRecord节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeUserRecordJson(u16 i)
    {
    	cJSON * pJson = NULL;
    	pJson = cJSON_CreateObject();
    	if(NULL == pJson)return NULL;
    	/* id */
    	cJSON_AddNumberToObject(pJson,"Id",userTab.array[i].id);
    	/* 是否启用 */
    	cJSON_AddBoolToObject(pJson,"Isuse",userTab.array[i].isuse);
    	/* 接口 */
    	cJSON_AddNumberToObject(pJson,"Interfac",userTab.array[i].interfac);
    	/* 通信ID */
    	cJSON_AddNumberToObject(pJson,"Commid",userTab.array[i].commid);
    	/* 串口参数 */
    	userTab.array[i].uart_params[TAB_UARTPARAMS_NUM-1] = 0;
    	cJSON_AddStringToObject(pJson,"Uart_params",userTab.array[i].uart_params);
    	/* 超时时间 */
    	cJSON_AddNumberToObject(pJson,"Timeout",userTab.array[i].timeout);
    	/* 传感器型号 和管理员表中的model相同 */
    	userTab.array[i].model[ROOT_TAB_SENIOR_LEN-1] = 0;
    	cJSON_AddStringToObject(pJson,"Model",userTab.array[i].model);
    	/* 备注 和管理员表中的note相同 */
    	userTab.array[i].note[TAB_NOTE_NUM-1] = 0;
    	cJSON_AddStringToObject(pJson,"Note",userTab.array[i].note);
    	/* 告警参数 */
    	cJSON_AddItemToObject(pJson,"Warning_params",makeWarningTypedefJson(i));
    	/* 联动输出 */
    	cJSON_AddItemToObject(pJson,"Linkage_params",makeLinkAgeTypedefJson(i));
    	/* 协议参数 */
    	cJSON_AddItemToObject(pJson,"Amt_params",makeAmtParamsTypedefJson(i));
    	return pJson;
    }
    
    /**
     * @name: 解析UserRecord节点 
     * @param {type} 
     * @return: 返回pJson:成功 NULL:失败
     */
    cJSON *parseUserRecordJson(cJSON *pJson,u16 i)
    {
    	cJSON * item = NULL;
    	u8 len=0;
    
    	/* id */
    	item = cJSON_GetObjectItem(pJson,"Id");
    	if(NULL == item)return NULL;
    	userTab.array[i].id = item->valueint;
    	/* 是否启用 */
    	item = cJSON_GetObjectItem(pJson,"Isuse");
    	if(NULL == item)return NULL;
    	userTab.array[i].isuse = item->valueint;
    	/* 接口 */
    	item = cJSON_GetObjectItem(pJson,"Interfac");
    	if(NULL == item)return NULL;
    	userTab.array[i].interfac = item->valueint;
    	/* 通信ID */
    	item = cJSON_GetObjectItem(pJson,"Commid");
    	if(NULL == item)return NULL;
    	userTab.array[i].commid = item->valueint;
    	/* 串口参数 */
    	item = cJSON_GetObjectItem(pJson,"Uart_params");
    	if(NULL == item)return NULL;
    	len = strlen(item->valuestring);
    	if(len>(TAB_UARTPARAMS_NUM-1))len=TAB_UARTPARAMS_NUM-1;
    	memcpy(userTab.array[i].uart_params,item->valuestring,len);
    	userTab.array[i].uart_params[len] = 0;
    	/* 超时时间 */
    	item = cJSON_GetObjectItem(pJson,"Timeout");
    	if(NULL == item)return NULL;
    	userTab.array[i].timeout = item->valueint;
    	/* 传感器型号 和管理员表中的model相同 */
    	item = cJSON_GetObjectItem(pJson,"Model");
    	if(NULL == item)return NULL;
    	len = strlen(item->valuestring);
    	if(len>(ROOT_TAB_SENIOR_LEN-1))len=ROOT_TAB_SENIOR_LEN-1;
    	memcpy(userTab.array[i].model,item->valuestring,len);
    	userTab.array[i].model[len] = 0;
    	/* 备注 */
    	item = cJSON_GetObjectItem(pJson,"Note");
    	if(NULL == item)return NULL;
    	len = strlen(item->valuestring);
    	if(len>(TAB_NOTE_NUM-1))len=TAB_NOTE_NUM-1;
    	memcpy(userTab.array[i].note,item->valuestring,len);
    	userTab.array[i].note[len] = 0;
    	/* 告警参数 */
    	item = parseWarningTypedefJson(pJson,i);
    	if(NULL == item)return NULL;
    	/* 联动输出 */
    	item = parseLinkAgeTypedefJson(pJson,i);
    	if(NULL == item)return NULL;
    	/* 协议参数 */
    	item = parseAmtParamsTypedefJson(pJson,i);
    	if(NULL == item)return NULL;
    	return pJson;
    }
    
    
    /**
     * @name: 创建CacheRecord节点 
     * @param {type} 
     * @return: cJSON
     */
    cJSON *makeCacheRecordJson()
    {
    	u16 i;
    	cJSON * pJson = NULL;
    	cJSON * item = NULL;
    	pJson = cJSON_CreateArray();
    	if(NULL == pJson)return NULL;
    	for (i = 0; i < userTab.num; i++)
    	{
    		item = cJSON_CreateObject();
    		if(NULL == item)break;
    		/* 数据区长度 */
    		cJSON_AddNumberToObject(item,"Data_len",cacheTab[i].data_len);
    		/* 数据区 */
    		cacheTab[i].data[CACHE_DATA_SIZE-1] = 0;
    		cJSON_AddStringToObject(item,"Data",cacheTab[i].data);
    		if(userTab.array[i].amt_params.modulus.type == VALUE_FLOAT)cJSON_AddNumberToObject(item,"Value",cacheTab[i].valuefloat);
    		else cJSON_AddNumberToObject(item,"Value",cacheTab[i].valueint);
    		/* 错误计数 */
    		cJSON_AddNumberToObject(item,"Err_num",cacheTab[i].err_num);
    		/* 在线状态 */
    		cJSON_AddBoolToObject(item,"Isonline",cacheTab[i].isonline);
    		/* 报警状态 */
    		cJSON_AddBoolToObject(item,"Iswarning",cacheTab[i].iswarning);
    		cJSON_AddItemToArray(pJson,item);
    	}
    	
    	return pJson;
    }
    
    //保存用户表到spiflash
    void save_user_tab()
    {
    	OS_CPU_SR cpu_sr = 0;
    	OS_ENTER_CRITICAL();
    	W25QXX_Write((u8*)&userTab,USERCONFIGTAB_ADDR,USERCONFIGTAB_SIZE);
    	OS_EXIT_CRITICAL();
    }
    
    
    
    //删除全部记录 user表
    u8 userTab_delete_all_record(void)
    {
    	memset(&userTab,0,(USERCONFIGTAB_SIZE));
    	return 0;
    }
    
    /**
     * @name: 根据id fun reg regLen 获取缓存的下标
     * @test: test font
     * @msg: 
     * @param {type} 
     * @return: 
     */
    u16 get_cacheTab_index(u16 id,u8 fun,u16 reg,u16 regLen)
    {
    
    }
    
    /**
     * @name: 串口参数解析
     * @msg: "9600,8,1,0" 解析出串口配置参数
     * @param 串口字符串,字符串长度。解析的结果 
     * @return: 0成功 其他失败
     */
    u8 parse_uartConfig_string(u8* string,u8 stringLen,u32* bps,u8* dataLen,u8* stopLen,u8* parity)
    {
    	char *p0,*p1;
    	u8 i;
    	u8 endChar=string[stringLen-1];
    	if(string == NULL)return 1;
    	string[stringLen-1]=',';//最后一位 我手动加一个逗号
    	p0 = (char*)string;
    	//取出参数
    	for(i=0;i<4;i++)
    	{
    		p1 = strstr(p0,",");
    		*p1=0;//结束
    		if(p0 != NULL)
    		{
    			if(i==0)*bps = atoi(p0);
    			else if(i==1)*dataLen = atoi(p0);
    			else if(i==2)*stopLen = atoi(p0);		
    			else if(i==3)*parity = atoi(p0);	
    			*p1=',';//用完再恢复
    			p0=p1+1;
    		}
    		else 
    		{
    			string[stringLen-1]=endChar;
    			return 1;
    		}
    	}
    	string[stringLen-1]=endChar;
    	return 0;
    }
    

    转换出来的json字符串如下:

    {
    	"Root":	{
    		"Variable":	[{
    				"Id":	1,
    				"Isuse":	true,
    				"Interfac":	0,
    				"Commid":	13,
    				"Uart_params":	"4800,8,1,0",
    				"Timeout":	100,
    				"Model":	"WS-N01-5-LCD",
    				"Note":	"液晶温湿度",
    				"Warning_params":	{
    					"Type":	0,
    					"Valtype":	0,
    					"Valueint":	0
    				},
    				"Linkage_params":	{
    					"Do0":	false,
    					"Dt0":	1,
    					"Do1":	false,
    					"Dt1":	0,
    					"Do2":	false,
    					"Dt2":	0,
    					"Do3":	false,
    					"Dt3":	0
    				},
    				"Amt_params":	{
    					"Type":	1,
    					"Tag":	"湿度",
    					"Fun":	3,
    					"Reg_addr":	0,
    					"Reg_len":	1,
    					"Unit":	"%",
    					"Modulus":	{
    						"Type":	2,
    						"Valuefloat":	0.100000
    					}
    				}
    			}, {
    				"Id":	2,
    				"Isuse":	true,
    				"Interfac":	0,
    				"Commid":	13,
    				"Uart_params":	"4800,8,1,0",
    				"Timeout":	100,
    				"Model":	"01-5-LCD",
    				"Note":	"液晶温湿度",
    				"Warning_params":	{
    					"Type":	0,
    					"Valtype":	0,
    					"Valueint":	0
    				},
    				"Linkage_params":	{
    					"Do0":	false,
    					"Dt0":	0,
    					"Do1":	false,
    					"Dt1":	0,
    					"Do2":	false,
    					"Dt2":	0,
    					"Do3":	false,
    					"Dt3":	0
    				},
    				"Amt_params":	{
    					"Type":	1,
    					"Tag":	"温度",
    					"Fun":	3,
    					"Reg_addr":	1,
    					"Reg_len":	1,
    					"Unit":	"℃",
    					"Modulus":	{
    						"Type":	2,
    						"Valuefloat":	0.100000
    					}
    				}
    			}]
    	}
    }
    

    5.splitContainer的间隔如何减小?

    在这里插入图片描述
    修改SplitterWidth=1即可
    在这里插入图片描述
    如果lable标签的Dock属性设置为fill,还是无法填满这个panel的话,肯定是autoSize属性没有设置为false
    在这里插入图片描述

    6.设置DataGridView单元格的内容居中显示

    DaatGridView.RowsDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
    

    7.列名称根据数据源的顺序

    dataGridView1.AutoGenerateColumns = false;
    

    8.datagridview显示绑定数据源的某几个属性的方法

    https://blog.csdn.net/weixin_33878457/article/details/93605504

    9.datagridview相关设置

    禁止编辑单元格 readOnle=true即可
    修改行高 e.Row.Cells[i].Height = 20;
    选中一行 DataGridViewSelectionMode.FullRowSelect;//设置为整行被选中码
    修改行背景颜色 this.dgvCache.Rows[row].DefaultCellStyle.BackColor = color;
    修改单元格背景颜色 this.dgvCache.Rows[row].Cells[cell].Style.BackColor = color;
    清空数据源,但是保留表头:

     			//保留表头
                try
                {
                    DataTable dt_xx = (DataTable)dgvCache.DataSource;
                    dt_xx.Rows.Clear();
                    dgvCache.DataSource = dt_xx;
                }
                catch (Exception)
                {
                }
                //this.dgvCache.DataSource = null;//不要使用这个
    
    展开全文
  • c#上位机开发1

    万次阅读 多人点赞 2018-10-08 11:32:36
    1、新建C#项目工程  首先选择新建Windows窗体应用(.NET Framework),然后选择项目保存位置,填写项目名称,这里因为我们不需要用git进行版本管理,所以不用新建GIT存储库;  框架是指.net框架,4以及4以下的.NET...

    本文来自https://www.cnblogs.com/Mculover666/p/9126191.html

    1、新建C#项目工程

       首先选择新建Windows窗体应用(.NET Framework),然后选择项目保存位置,填写项目名称,这里因为我们不需要用git进行版本管理,所以不用新建GIT存储库;

       框架是指.net框架,4以及4以下的.NET框架可以在xp上运行,4以上可以在win7/8/10上运行,鉴于当前大多数操作系统都是win7或win10,选择4.5版本。

    2、窗体介绍及代码分析

    这里我们双击窗体界面,这也是VS的特性,双击一个控件,就会进入对应代码文件部分,这些代码全由VS在生成项目时自动生成,下面进行详细的解释:

    复制代码

     1 /*filename:Form1.cs*/
     2 //使用命名空间
     3 using System;
     4 using System.Collections.Generic;
     5 using System.ComponentModel;
     6 using System.Data;
     7 using System.Drawing;
     8 using System.Linq;
     9 using System.Text;
    10 using System.Windows.Forms;
    11 
    12 //用户项目工程自定义命名空间HelloWorld
    13 namespace HelloWorld
    14 {
    15     //定义了一个名称为Form1的公共类,并且在定义类的同时创建了一个这个类的对象,名为Form
    16     //partial关键字
    17     public partial class Form1 : Form
    18     {
    19         //与类同名的构造方法
    20         public Form1()
    21         {
    22             InitializeComponent();
    23         }
    24      //用户自定义方法,窗体加载时由Form对象调用
    25         private void Form1_Load(object sender, EventArgs e)
    26         {
    27         }
    28     }
    29 }

    复制代码

      命名空间(namespace):在C#中用命名空间将很多类的属性及其方法进行封装供调用,类似C语言中将变量和函数封装成一个个.h文件,调用的时候只需要#include "filepath + filename"就可以使用,比如刚开始时用关键字using声明了一些所需要的系统命名空间(line1-10);然后采用关键字namespace来自定义一个用户工程所需的命名空间HelloWorld,在我们定义的这个命名空间里就可以定义一些类和方法来进行下一步的实现;

      类(class:C#是一门面向对象的编程语言,所以最基本的就是类和对象,对象的特征是具有属性(C语言中称为变量)和方法(C语言中称为函数),然后我们定义一个类来描述这个对象的特征,注意:这个时候定义的类不是真实存在的,所以不会分配内存空间,当我们用所定义的这个类去创建一个类的对象,这个对象是真实存在的,它会占用内存空间,比如在这个工程中定义了一个名称为Form1的公共类,并且在定义类的同时创建了一个这个类的对象,名为Form;

      方法:前面已经说过,在面向对象编程中是没有变量和函数的,所有的函数都被封装在类中,属于对象的方法,最基本的是类的构造方法,该方法与类名同名,在用类创建一个具体对象时自动调用,不可缺少,比如Form1( );另外一种是自己定义的用户方法,比如该类中的Form1_Load()方法,就是在初始化窗口时,通过具体对象Form调用:Form.Form1_Load( );

      访问修饰符:用来控制类、属性、方法的访问权限,常用有5个,默认私有,不能被外部访问;

        私有的private,公共的public,受保护的protected,内部的internal,受保护内部的protect internal

     

      这里有一个重点,在定义Form1类的时候含有一个关键字partial,这里就不得不说C#语言设计一个重要的特性了,能作为大多数人开发上位机的首选,C#有一个特性就是设计的时候界面与后台分离,但是类名相同,首先看一下工程文件结构:

    可以看到,Form1.cs文件下面包含了另一个Form1.Designer.cs文件,再打开Form1.Designer.cs这个文件,是不是很惊奇,和前面一模一样,再次定义了一个命名空间HelloWorld和Form1类,这个部分类中定义了我们使用的控件、事件委托以及如Dispose方法等。因为这里面的代码都是自动生成的,因此设计成了一个部分类。最关键的一点,这里类也是用partial关键字修饰的,可以看到,Partial是局部类型的意思,允许我们将一个类、结构或接口分成几个部分,分别实现在几个不同的.cs文件中,用partial定义的类可以在多个地方被定义,最后C#编译器编译时会将这些类当作一个类来处理;

    复制代码

     1 /*@filename:Form1.Designer.cs */
     2 
     3 namespace HelloWorld
     4 {
     5     partial class Form1
     6     {
     7         /// <summary>
     8         /// 必需的设计器变量。
     9         /// </summary>
    10         private System.ComponentModel.IContainer components = null;
    11 
    12         /// <summary>
    13         /// 清理所有正在使用的资源。
    14         /// </summary>
    15         /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
    16         protected override void Dispose(bool disposing)
    17         {
    18             if (disposing && (components != null))
    19             {
    20                 components.Dispose();
    21             }
    22             base.Dispose(disposing);
    23         }
    24 
    25         #region Windows 窗体设计器生成的代码
    26 
    27         /// <summary>
    28         /// 设计器支持所需的方法 - 不要修改
    29         /// 使用代码编辑器修改此方法的内容。
    30         /// </summary>
    31         private void InitializeComponent()
    32         {
    33             this.SuspendLayout();
    34             // 
    35             // Form1
    36             // 
    37             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
    38             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    39             this.ClientSize = new System.Drawing.Size(418, 331);
    40             this.Name = "Form1";
    41             this.Text = "Form1";
    42             this.Load += new System.EventHandler(this.Form1_Load);
    43             this.ResumeLayout(false);
    44 
    45         }
    46         #endregion
    47     }
    48 }

    复制代码

      

      Main: 一切程序都有入口主函数main,C#也是如此,在Program.cs文件中定义了Program类,该类中拥有主函数main( ), 在main函数中,第三行代码是一切的开始,调用Form1类的构造函数,创建一个Form对象,一切由此开始,代码如下:

    复制代码

     1 /* @filename: Program.cs */
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Linq;
     5 using System.Windows.Forms;
     6 
     7 namespace HelloWorld
     8 {
     9     static class Program
    10     {
    11         /// <summary>
    12         /// 应用程序的主入口点。
    13         /// </summary>
    14         [STAThread]
    15         static void Main()
    16         {
    17             Application.EnableVisualStyles();
    18             Application.SetCompatibleTextRenderingDefault(false);
    19             Application.Run(new Form1());  //调用Form1类的构造函数,创建一个Form对象,一切由此开始
    20         }
    21     }
    22 }

    复制代码

     

      再来解释一下最后三个文件:第一个文件主要是应用程序发布时的一些属性设置,版本号,属性,版权之类的,其余两个文件是工具自动生成的一些设置文件,不再过多赘述;

    复制代码

    /* @filename:Assemblylnfo.cs*/
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    
    // 有关程序集的一般信息由以下
    // 控制。更改这些特性值可修改
    // 与程序集关联的信息。
    [assembly: AssemblyTitle("HelloWorld")]
    [assembly: AssemblyDescription("")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("")]
    [assembly: AssemblyProduct("HelloWorld")]
    [assembly: AssemblyCopyright("Copyright ©  2018")]
    [assembly: AssemblyTrademark("")]
    [assembly: AssemblyCulture("")]
    
    // 将 ComVisible 设置为 false 会使此程序集中的类型
    //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
    //请将此类型的 ComVisible 特性设置为 true。
    [assembly: ComVisible(false)]
    
    // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
    [assembly: Guid("094ac56a-7a59-4f32-a2eb-857135be4d2c")]
    
    // 程序集的版本信息由下列四个值组成: 
    //
    //      主版本
    //      次版本
    //      生成号
    //      修订号
    //
    // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
    // 方法是按如下所示使用“*”: :
    // [assembly: AssemblyVersion("1.0.*")]
    [assembly: AssemblyVersion("1.0.0.0")]
    [assembly: AssemblyFileVersion("1.0.0.0")]

    复制代码

     

    3、Hello,World

      下面就正式开始C#程序的设计,首先是界面的实现,可以随意从控件工具箱中拖放控件到窗体中,这里我拖动两个Button和一个TextBox,并在右边设置框中修改每个控价的属性,界面如图:

    可以看到,Form1.cs文件下面包含了另一个Form1.Designer.cs文件,再打开Form1.Designer.cs这个文件,是不是很惊奇,和前面一模一样,再次定义了一个命名空间HelloWorld和Form1类,这个部分类中定义了我们使用的控件、事件委托以及如Dispose方法等。因为这里面的代码都是自动生成的,因此设计成了一个部分类。最关键的一点,这里类也是用partial关键字修饰的,可以看到,Partial是局部类型的意思,允许我们将一个类、结构或接口分成几个部分,分别实现在几个不同的.cs文件中,用partial定义的类可以在多个地方被定义,最后C#编译器编译时会将这些类当作一个类来处理;

    复制代码

     1 /*@filename:Form1.Designer.cs */
     2 
     3 namespace HelloWorld
     4 {
     5     partial class Form1
     6     {
     7         /// <summary>
     8         /// 必需的设计器变量。
     9         /// </summary>
    10         private System.ComponentModel.IContainer components = null;
    11 
    12         /// <summary>
    13         /// 清理所有正在使用的资源。
    14         /// </summary>
    15         /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
    16         protected override void Dispose(bool disposing)
    17         {
    18             if (disposing && (components != null))
    19             {
    20                 components.Dispose();
    21             }
    22             base.Dispose(disposing);
    23         }
    24 
    25         #region Windows 窗体设计器生成的代码
    26 
    27         /// <summary>
    28         /// 设计器支持所需的方法 - 不要修改
    29         /// 使用代码编辑器修改此方法的内容。
    30         /// </summary>
    31         private void InitializeComponent()
    32         {
    33             this.SuspendLayout();
    34             // 
    35             // Form1
    36             // 
    37             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
    38             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    39             this.ClientSize = new System.Drawing.Size(418, 331);
    40             this.Name = "Form1";
    41             this.Text = "Form1";
    42             this.Load += new System.EventHandler(this.Form1_Load);
    43             this.ResumeLayout(false);
    44 
    45         }
    46         #endregion
    47     }
    48 }

    复制代码

      

      Main: 一切程序都有入口主函数main,C#也是如此,在Program.cs文件中定义了Program类,该类中拥有主函数main( ), 在main函数中,第三行代码是一切的开始,调用Form1类的构造函数,创建一个Form对象,一切由此开始,代码如下:

    复制代码

     1 /* @filename: Program.cs */
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Linq;
     5 using System.Windows.Forms;
     6 
     7 namespace HelloWorld
     8 {
     9     static class Program
    10     {
    11         /// <summary>
    12         /// 应用程序的主入口点。
    13         /// </summary>
    14         [STAThread]
    15         static void Main()
    16         {
    17             Application.EnableVisualStyles();
    18             Application.SetCompatibleTextRenderingDefault(false);
    19             Application.Run(new Form1());  //调用Form1类的构造函数,创建一个Form对象,一切由此开始
    20         }
    21     }
    22 }

    复制代码

     

      再来解释一下最后三个文件:第一个文件主要是应用程序发布时的一些属性设置,版本号,属性,版权之类的,其余两个文件是工具自动生成的一些设置文件,不再过多赘述;

    复制代码

    /* @filename:Assemblylnfo.cs*/
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    
    // 有关程序集的一般信息由以下
    // 控制。更改这些特性值可修改
    // 与程序集关联的信息。
    [assembly: AssemblyTitle("HelloWorld")]
    [assembly: AssemblyDescription("")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("")]
    [assembly: AssemblyProduct("HelloWorld")]
    [assembly: AssemblyCopyright("Copyright ©  2018")]
    [assembly: AssemblyTrademark("")]
    [assembly: AssemblyCulture("")]
    
    // 将 ComVisible 设置为 false 会使此程序集中的类型
    //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
    //请将此类型的 ComVisible 特性设置为 true。
    [assembly: ComVisible(false)]
    
    // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
    [assembly: Guid("094ac56a-7a59-4f32-a2eb-857135be4d2c")]
    
    // 程序集的版本信息由下列四个值组成: 
    //
    //      主版本
    //      次版本
    //      生成号
    //      修订号
    //
    // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
    // 方法是按如下所示使用“*”: :
    // [assembly: AssemblyVersion("1.0.*")]
    [assembly: AssemblyVersion("1.0.0.0")]
    [assembly: AssemblyFileVersion("1.0.0.0")]

    复制代码

     

    3、Hello,World

      下面就正式开始C#程序的设计,首先是界面的实现,可以随意从控件工具箱中拖放控件到窗体中,这里我拖动两个Button和一个TextBox,并在右边设置框中修改每个控价的属性,界面如图:

    展开全文
  • C#上位机开发(一)—— 了解上位机

    万次阅读 多人点赞 2018-06-02 16:07:00
    在单片机项目开发中,上位机也是一个很重要的部分,主要用于数据显示(波形、温度等)、用户控制(LED,继电器等),下位机(单片机)与 上位机之间要进行数据通信的两种方式都是基于串口的: USB转串口 —— ...
  • c#开发的一个简单的上位机程序,详细的注释,方便学习使用
  • C#上位机源代码

    2018-03-14 17:21:52
    void Send_toMy(short a,short b,short c,short d,short e,short f) { u8 tbuf[14]; tbuf[0]=0XAA; tbuf[1]=0XAA; tbuf[2]=(a>>8)&0XFF; tbuf[3]=a&0XFF; tbuf[4]=(b>>8)&0XFF; tbuf[5]=b&0XFF;...}
  • 最近想做一个基于嵌入式调试串口发送数据的上位机,所以就开始了学习C#Wpf的路程 一、准备工作 安装Microsoft visual studio,对于版本的选择,我的建议还是老一些的版本比较好,这样出了问题之后可以百度找到解决...
  • VS2019+C#上位机开发

    2021-05-28 15:55:36
    C# 开发窗口调试助手 VS2019 中新建C#上位机工程 ,默认没有SerialPort这个对象,需要自己手动添加,具体添加过程如下 工具->NuGet包管理器->管理解决方案的NuGet包,在浏览页面下,下载SerialPort需要的...
  • C#上位机开发--保存日志

    万次阅读 2018-08-28 10:20:35
    既然差不多知道什么是上位机与下位机,那么,我们做到小喇叭的要求:就得写个上位机,我自己也是学了下C#,用来开发上位机还是可以的,开发环境用visual studio 2015,微软的软件真的是很简单,之前看到有人问为什么...
  • C#上位机开发登陆系统主要使用SqlServer实现,实现本地数据库的登录校验,注册功能仍待完善… 使用集成开发环境:Visual Studio 2019 数据库可视化管理:Sql Server 2019 一、窗体界面绘制 界面绘制如下: ...
  • C#上位机开发(一)—— 了解上位机 C#上位机开发(二)—— Hello,World C#上位机开发(三)—— 构建SerialAssistant雏形 C#上位机开发(四)—— SerialAssistant功能完善 这个系列存在的问题就是没有进行界面...
  • C#上位机开发视频,包含上位机串口助手开发,串口控制下位机,PC串口接收发送数据等的视频讲解,还有C#教程资料
  • 一、表格控件的基本使用方法 1. 添加控件 添加一个DataGridView控件: 为了方便使用,将该控件铺满整个窗口(Dock属性设为fill): 2. 设置列数、列名 添加初始化函数: private void InitGridView() ...
  • 上位机编程C#

    2019-01-22 15:49:15
    21G的百度网盘资源,里面包含C#视频教程,源码,实例,包括vs2010安装文件,需要的拿走
  • 开始之前先讲一下汉字编码到底是个啥? 汉字编码就是汉字对应于计算机中的编码存储,主要有以下几种常见的汉字字符集编码: GB2312编码:1981年5月1日发布的简体中文汉字编码国家标准。GB2312对汉字采用双字节编码...
  • (1)上位机界面实现 界面看起来很简单,可以自己怎么喜欢怎么放置,主要有3个组件: label显示“数据”二字 下拉框用于选择要发送的数据(主要为了体现使用下拉框) 按钮点击发送数据操作 重点是串口加入,...
  • 1. 界面设计 2. 使用多线程修改进度条 引入多线程类库命名空间: using System.Threading; 将变量添加到Form1类: private Thread th; ...将方法添加到Form1类: private void ThreadTask() ... stp = this.progressBa
  • 基于C#做的上位机

    2018-05-04 15:42:16
    C#做了一个简单的串口助手,并且有分析协议的能力。在我学习C#两个礼拜后根据实际项目来做的一个上位机
  • C#上位机开发资料和源码,视频讲解开发案例和源码,通讯基础知识,协议有OPC,modbus,S7,socket适合PLC工程师,自动化工程师,MES维护等人员学习,掌握开发PLC和PC通讯主流技术id=661677380399&
  • C#上位机界面

    2018-04-07 16:11:05
    利用C#制作的一个简易操作见面,可供大家模仿使用,如有问题欢迎咨询
  • C#上位机开发(一)—— 了解上位机 C#上位机开发(二)—— Hello,World C#上位机开发(三)—— 构建SerialAssistant雏形 C#上位机开发(四)—— SerialAssistant功能完善 C#上位机开发(五)——...
  • 上一篇大致了解了一下单片机实际项目开发中上位机开发部分的内容以及VS下载与安装,按照编程惯例,接下来就是“Hello,World!” 1、新建C#项目工程  首先选择新建Windows窗体应用(.NET Framework),然后选择项目...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,907
精华内容 1,162
关键字:

c#上位机开发

c# 订阅