zoukankan      html  css  js  c++  java
  • C#作为服务器端 MATLAB作为客户端 二者之间进行通信

        之前的socket通信,一直是在visual studio的C#平台进行的,服务器端是简单的增量式PID控制器(这个算法,后续应该会再深入学习,然后将算法丰富),可是现在,老师让实现这样一个功能,在另一台计算机上有一搭建好的电机模型,要实时的将电机的速度拿到,作为PID控制器的输入,实时速度与设定值比较,得到的偏差作为PID控制器的输入,PID控制器计算后的输出值再给电机的输入端,从而通过这样的闭环控制,来控制电机(涉及到一个D/A转换,这个现在还没设计。但猜想是不是可以直接通过一个零阶保持器来实现)。 

        电机的实时速度,需要从simulink模块中提取出来,传给.m文件,从而通过.m文件与C#之间的通信,实现PID控制功能。

        C#服务器端代码改变不大,为了方便,直接拿之前做的winform过来,在那基础上改写的,有的代码没用到,也没删掉,核心是Receive( )函数。

    C#服务器端winform代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using System.Collections;
    using System.IO;
    
    namespace WindowsForms_服务器端
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            double[] nums = new double[3];//存储3个偏差值e(k) e(k-1) e(k-2)
            double actual = 0;
            double set = 0;
            double kp = 0.4;
            double ki = 0.53;
            double kd = 0.1;
            //定义一个空字节数组date作为数据缓冲区,用于缓冲流入和流出的信息
            byte[] date = new byte[100];
            static Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            static Socket client;
            ArrayList list = new ArrayList();//存储客户端发来的速度设定值
            ArrayList listOut = new ArrayList();//存储PID计算的实际速度值
            Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();//创建键值对,存储套接字的ip地址等信息
    
            Thread thConnect;
    
            /// <summary>
            /// 建立与客户端的连接
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void buttonConnect_Click(object sender, EventArgs e)
            {
                try
                {
                    IPEndPoint Ipep = new IPEndPoint(IPAddress.Any, 125);//指定地址和端口号
                    newsock.Bind(Ipep);
                    newsock.Listen(10);//置于监听状态
                    textState.Text = "等待客户端的连接
    ";
                    thConnect = new Thread(Connect);
                    thConnect.IsBackground = true;
                    thConnect.Start();
                }
                catch
                { }
            }
    
            int i = 0;
    
            /// <summary>
            /// 不断接收从客户端发来消息的方法
            /// </summary>
            void Receive()
            {
                try
                {
                    while (true)
                    {
                        int reci = client.Receive(date);
                        if (date.Length == 0)
                        {
                            break;
                        }
                        string str = Encoding.ASCII.GetString(date,0,reci);
                        //if(Encoding.ASCII.GetString(date,0,date.Length)=="a")//证明MATLAB端已经写入成功实时速度
                        //else
                        if (str == "a")
                        {
                            FileStream fs = new FileStream(@"C:Users
    euDesktop	est1.txt", FileMode.Open);
                            int count = (int)fs.Length;
                            byte[] readData = new byte[count];
                            fs.Read(readData, 0, count);
                            string strNew = Encoding.ASCII.GetString(readData);
                            fs.Close();//使用完要关闭文件 否则会一直占用资源
                            //File.Delete(@"C:Users
    euDesktop	est1.txt");
    
                            actual = double.Parse(strNew);
                            actual = actual * actual;
                            date = System.Text.Encoding.UTF8.GetBytes(actual.ToString());
    
                            //在这里,下一步要将PID控制输出的速度值,传给MATLAB端 即计算一个,输出一个到MATLAB端
                            textSend.AppendText(actual.ToString() + "
    ");
                            FileStream fsWrite = new FileStream(@"C:Users
    euDesktop	est1.txt", FileMode.OpenOrCreate);
                            fsWrite.Write(date, 0, date.Length);
                            fsWrite.Close();
                            //写入成功后 即PID计算完成后 给MATLAB端发送一信号 告诉它可以往下继续执行了
                            date = System.Text.Encoding.ASCII.GetBytes("1");
                            client.Send(date, date.Length, SocketFlags.None);
                        }
                    }
                }
                catch
                { }
            }
    
            /// <summary>
            /// 服务器端与客户端连接的方法  使一个服务器可以与多个客户端连接
            /// </summary>
            private void Connect()
            {
                //接收来自客户端的接入尝试连接,并返回连接客户端的ip地址
                while (true)
                {
                    client = newsock.Accept();
                    IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
                    //返回客户端的ip地址和端口号
                    textState.AppendText("与" + clientep.Address + "在" + clientep.Port + "端口连接
    ");
                    dicSocket.Add(clientep.ToString(), client);//将连接的客户端的IP地址添加在键值对集合中
                    comboBox.Items.Add(clientep);//将客户端的IP地址显示在下拉栏中
                    comboBox.SelectedIndex = 0;
                    Thread thReceive = new Thread(Receive);//创建一个新线程 执行Receive()方法
                    thReceive.IsBackground = true;
                    thReceive.Start();
                }
            }
    
    
            /// <summary>
            /// 发送消息给客户端 需自己选择已连接的其中一个客户端
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void buttonSend_Click(object sender, EventArgs e)
            {
                try
                {
                    string str = textSend.Text;
                    date = System.Text.Encoding.UTF8.GetBytes(str); //以字节数组的形式 将欢迎信息发送给客户端 注意发送与接收要用相同的编码格式UTF8 否则会乱码
                    try
                    {
                        string ip = comboBox.SelectedItem.ToString();
                        SendVarMessage(dicSocket[ip], date);
                    }
                    catch
                    {
                        MessageBox.Show("发送失败,请确认已选择一个客户端");
                    }
                }
                catch
                { }
            }
    
    
            /// <summary>
            /// 断开与客户端的连接
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void buttonBreak_Click(object sender, EventArgs e)
            {
                try
                {
                    client.Close();
                    newsock.Close();
                    textState.AppendText("断开连接
    ");
                    this.Close();
                }
                catch { }
            }
    
            /// <summary>
            /// 跨线程访问
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Form1_Load(object sender, EventArgs e)
            {
                Control.CheckForIllegalCrossThreadCalls = false;
            }
    
            private void textState_TextChanged(object sender, EventArgs e)
            {
    
            }
    
            private void textReceive_TextChanged(object sender, EventArgs e)
            {
    
            }
    
            double outSpeed = 0;//PID控制器计算后的输出量,以后要传给模型输入端,以控制模型
    
            /// <summary>
            /// 开始进行PID计算
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void button1_Click(object sender, EventArgs e)
            {
            }
    
    
            /// <summary>
            /// 发送变长消息方法
            /// </summary>
            /// <param name="s"></param>
            /// <param name="msg"></param>
            /// <returns></returns>
            private static void SendVarMessage(Socket s, byte[] msg)
            {
                int offset = 0;
                int sent;
                int size = msg.Length;
                int dataleft = size;
                byte[] msgsize = new byte[2];
    
                //将消息的尺寸从整型转换成可以发送的字节型
                //因为int型是占4个字节 所以msgsize是4个字节 后边是空字节
                msgsize = BitConverter.GetBytes(size);
    
                //发送消息的长度信息
                //之前总是乱码出错 客户端接收到的欢迎消息前两个字节是空 后边的两个字符er传送到第二次接收的字节数组中
                //因此将er字符转换为int出错  这是因为之前在Send代码中,是将msgsize整个字节数组发送给客户端 所以导致第3 4个空格也发送
                //导致发送的信息混乱 这两个空格使发送的信息都往后挪了两个位置  从而乱码
                sent = s.Send(msgsize, 0, 2, SocketFlags.None);
                while (dataleft > 0)
                {
                    int sent2 = s.Send(msg, offset, dataleft, SocketFlags.None);
                    //设置偏移量
                    offset += sent2;
                    dataleft -= sent2;
                }
                //return dataleft;
            }
    
            /// <summary>
            /// 接收变长消息方法
            /// </summary>
            /// <param name="s"></param>
            /// <returns>接收到的信息</returns>
            private static byte[] ReceiveVarMessage(object o)//方法的返回值是字节数组 byte[] 存放的是接受到的信息
            {
                Socket s = o as Socket;
                int offset = 0;
                int recv;
                byte[] msgsize = new byte[2];
    
                //接收2个字节大小的长度信息
                recv = s.Receive(msgsize, 0, 2, 0);
    
                //将字节数组的消息长度转换为整型
                int size = BitConverter.ToInt16(msgsize, 0);
                int dataleft = size;
                byte[] msg = new byte[size];
                while (dataleft > 0)
                {
                    //接收数据
                    recv = s.Receive(msg, offset, dataleft, 0);
                    if (recv == 0)
                    {
                        break;
                    }
                    offset += recv;
                    dataleft -= recv;
                }
                return msg;
            }
    
            private void textSend_TextChanged(object sender, EventArgs e)
            {
    
            }
        }
    }
    

    MATLAB .m文件代码:

    s = tcpip('127.0.0.1', 125, 'NetworkRole','client');
    set(s, 'InputBufferSize', 30); 
    set(s, 'outputBufferSize', 30); 
    set(s,'Timeout',3);
    fopen(s);
    b=1;
    c=1;
    d=1;
    
    a='1.1';%模拟存储电机的实时速度
    fid=fopen('C:Users
    euDesktop	est1.txt','wb');
    fprintf(fid,'%s',a);%将实时速度写在文本文件中,从而服务器端可通过这个文本文件.txt读取到实时速度
    fclose(fid);
    fwrite(s,'a');%发送一个字符(随意的)给PID控制器,表示可以从TXT中读取传入的实时速度值了
    pause(1);%暂停一秒
    
    while(b)%while循环是想 只有读到PID控制器计算完成的信号后,再往下执行代码 否则在原地等待 知道读到信号
        read=fread(s,1)
        if read==49
            b=0;
        end
    end
    fid2=fopen('C:Users
    euDesktop	est1.txt','r');%读取PID控制器输出的计算值
    a=fscanf(fid2,'%f')%fscanf读取文本文件(.txt) a是double类型的数据  
    fclose(fid2);%关闭文件 即释放占用的资源
    
    a='2.2';%模拟存储电机的实时速度
    fid=fopen('C:Users
    euDesktop	est1.txt','wb');
    fprintf(fid,'%s',a);%将实时速度写在文本文件中,从而服务器端可读取到实时速度
    fclose(fid);
    fwrite(s,'a');
    pause(1);
    
    while(c)
        read=fread(s,1)
        if read==49
            c=0;
        end
    end
    fid2=fopen('C:Users
    euDesktop	est1.txt','r');%读取PID控制器输出的计算值
    a=fscanf(fid2,'%f')%fscanf读取文本文件(.txt) a是double类型的数据  
    fclose(fid2);%关闭文件 即释放占用的资源
    
    
    a='3.3';%模拟存储电机的实时速度
    fid=fopen('C:Users
    euDesktop	est1.txt','wb');
    fprintf(fid,'%s',a);%将实时速度写在文本文件中,从而服务器端可读取到实时速度
    fclose(fid);
    fwrite(s,'a');
    pause(1);
    
    while(d)
        read=fread(s,1)
        if read==49
            d=0;
        end
    end
    fid2=fopen('C:Users
    euDesktop	est1.txt','r');%读取PID控制器输出的计算值
    a=fscanf(fid2,'%f')%fscanf读取文本文件(.txt) a是double类型的数据  
    fclose(fid2);%关闭文件 即释放占用的资源
    
    
    fclose(s)
    

      

     因为还没实现从simulink仿真模块中拿到模拟电机的实时速度,所以这里是传递了三个给定的参数给C#服务器端,返回的是传递过去数值的平方,即返回值依次是1.21 ,4.84 ,10.89。    从仿真模块中拿到的实时速度,要先存储在一个变量中,然后再发送给服务器端,但是我用fwrite函数,只知道怎样发送一个数值或字符串,怎样将存储数值的变量发送过去还不知道,所以这里是用将实时速度用fprintf函数写入到文本文件.txt中,服务器端再从这个文本文件读数。

        下一步,将不必要的代码删掉,缕清楚程序。程序还存在一个问题是,客户端改为MATLAB后,缺少了在线修改参数的模块,因为MATLAB端不是界面,所以,想把之前放在C#客户端的修改参数模块,移至服务器端,服务器端是winform界面,修改很方便,若再编写一个客户端,只是实现修改参数功能,意义不大。

  • 相关阅读:
    反射学习(二)
    反射学习(一)
    web项目问题总结
    MyEclipse使用过程中的问题及对应设置的总结
    JSP的四个作用域
    bootstrap 点击模态框上的提交按钮后,模态框不能关闭的解决办法
    xgboost 参数
    CART树
    GridSearchCV
    xgboost使用
  • 原文地址:https://www.cnblogs.com/1987-05-04/p/6762384.html
Copyright © 2011-2022 走看看