S-Function实现simulink仿真与VC通信

在使用simulink仿真和其他语言编写的仿真模块合作时,总存在两种语言模块的数据交互的问题,本文考虑使用S-Function构建一个单独的通信模块,将该模块添加到simulink模型中,实现仿真数据的交互。

Matlab的simulink仿真有提供一个用户自定义模块,该模块可以用多种编程语言来实现,本文介绍:使用C++的Socket通信来编写代码,实现和Vc的交互。

1. VC++用户自定义模块的实现方法

a. 在模型中添加S-Function, 编写模块对应的函数代码

b、编译C++代码,在matlab中编译,需要先通过matlab命令行设置matlab的mex编译器,方法如下:

S-Function实现simulink仿真与VC通信

选择VS2005编译器,然后使用mex 命令来编译代码,命令格式:mex cppfile(模块对应的代码的文件名),编译成功会有相应的提示

c. 编译成功会产生一个后缀为mexw32的mex程序,有了这个程序,用户自定义模块就可以工作了

 

2. 例子

Demo说明:两个正弦输入信号经过mux模块集束成一个输入数组,经过自定义模块,最后到达Scope模块显示。在自定义模块(UseFunc)中,通过Socket采用UDP将输入数据发送到某个端口。

2.1 Simulink模型

S-Function实现simulink仿真与VC通信

S-Function代码:

UseFunc.h

/* Copyright 2003-2004 The MathWorks, Inc. */



#ifndef _SFUN_CPP_USER_DEFINE_CPP_

#define _SFUN_CPP_USER_DEFINE_CPP_



// Define a generic template that can accumulate

// values of any numeric data type

template <class DataType> class GenericAdder {

private:

    DataType Peak;

public:

    GenericAdder() {

        Peak = 0;

    }

    DataType AddTo(DataType Val) {

        Peak += Val;

        return Peak;

    }

    DataType GetPeak() {

        return Peak;

    }

};



// Specialize the generic adder to a 'double'

// data type adder

class DoubleAdder : public GenericAdder<double> {};



#endif

 

UseFunc.cpp

/* Copyright 2003-2004 The MathWorks, Inc. */



// *******************************************************************

// **** To build this mex function use: mex sfun_cppcount_cpp.cpp ****

// *******************************************************************





#include "UseFunc.h"



#define S_FUNCTION_LEVEL 2

#define S_FUNCTION_NAME  UseFunc



// Need to include simstruc.h for the definition of the SimStruct and

// its associated macro definitions.

#include "simstruc.h"

#include "mex.h" 





#ifndef WIN32_LEAN_AND_MEAN

#define WIN32_LEAN_AND_MEAN

#endif



#include <winsock2.h>

#include <Ws2tcpip.h>

#include <stdio.h>



// Link with ws2_32.lib

#pragma comment(lib, "Ws2_32.lib")



void UseFun_StartSock(SimStruct *S);

void UseFun_SentData(SimStruct *S, const real_T *data, int DataNum);

void UseFun_CloseSock(SimStruct *S);





#define IS_PARAM_DOUBLE(pVal) (mxIsNumeric(pVal) && !mxIsLogical(pVal) &&\

!mxIsEmpty(pVal) && !mxIsSparse(pVal) && !mxIsComplex(pVal) && mxIsDouble(pVal))



// Function: mdlInitializeSizes ===============================================

// Abstract:

//    The sizes information is used by Simulink to determine the S-function

//    block's characteristics (number of inputs, outputs, states, etc.).

static void mdlInitializeSizes(SimStruct *S)

{

    // No expected parameters

    ssSetNumSFcnParams(S, 0);



    // Parameter mismatch will be reported by Simulink

    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {

        return;

    }



    // Specify I/O

    if (!ssSetNumInputPorts(S, 1)) return;

    ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);

    ssSetInputPortDirectFeedThrough(S, 0, 1);

    if (!ssSetNumOutputPorts(S,1)) return;

    ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);



    ssSetNumSampleTimes(S, 1);



    // Reserve place for C++ object

    ssSetNumPWork(S, 3);



    ssSetOptions(S,

                 SS_OPTION_WORKS_WITH_CODE_REUSE |

                 SS_OPTION_EXCEPTION_FREE_CODE);



}





// Function: mdlInitializeSampleTimes =========================================

// Abstract:

//   This function is used to specify the sample time(s) for your

//   S-function. You must register the same number of sample times as

//   specified in ssSetNumSampleTimes.

static void mdlInitializeSampleTimes(SimStruct *S)

{

    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);

    ssSetOffsetTime(S, 0, 0.0);

    ssSetModelReferenceSampleTimeDefaultInheritance(S); 

}



// Function: mdlStart =======================================================

// Abstract:

//   This function is called once at start of model execution. If you

//   have states that should be initialized once, this is the place

//   to do it.

#define MDL_START

static void mdlStart(SimStruct *S)

{

    // Store new C++ object in the pointers vector

    DoubleAdder *da  = new DoubleAdder();

    ssGetPWork(S)[0] = da;



    UseFun_StartSock(S);

}



// Function: mdlOutputs =======================================================

// Abstract:

//   In this function, you compute the outputs of your S-function

//   block.

static void mdlOutputs(SimStruct *S, int_T tid)

{

    // Retrieve C++ object from the pointers vector

    DoubleAdder *da = static_cast<DoubleAdder *>(ssGetPWork(S)[0]);

    

    // Get data addresses of I/O

    InputRealPtrsType  u = ssGetInputPortRealSignalPtrs(S,0);

               real_T *y = ssGetOutputPortRealSignal(S, 0);



    int InputNum = ssGetInputPortWidth(S, 0);

    for(int i=0;i<InputNum;i++)

    {



        y[i] = *u[i];

    }



    UseFun_SentData(S, y, InputNum);

}



// Function: mdlTerminate =====================================================

// Abstract:

//   In this function, you should perform any actions that are necessary

//   at the termination of a simulation.  For example, if memory was

//   allocated in mdlStart, this is the place to free it.

static void mdlTerminate(SimStruct *S)

{

    // Retrieve and destroy C++ object

    DoubleAdder *da = static_cast<DoubleAdder *>(ssGetPWork(S)[0]);

    delete da;



    UseFun_CloseSock(S);

}



void UseFun_StartSock(SimStruct *S)

{

    int iResult;

    WSADATA wsaData;



    SOCKET *pSendSocket = new SOCKET;

    *pSendSocket = INVALID_SOCKET;



    sockaddr_in *pRecvAddr = new sockaddr_in;



    unsigned short Port = 27015;

    

    printf("Start socket communication, please wait...\n");



    //----------------------

    // Initialize Winsock

    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != NO_ERROR) {

        printf("WSAStartup failed with error: %d\n", iResult);

        return ;

    }



    //---------------------------------------------

    // Create a socket for sending data

    *pSendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (*pSendSocket == INVALID_SOCKET) {

        printf("socket failed with error: %ld\n", WSAGetLastError());

        WSACleanup();

        return ;

    }



    //---------------------------------------------

    // Set up the RecvAddr structure with the IP address of

    // the receiver (in this example case "192.168.1.1")

    // and the specified port number.

    pRecvAddr->sin_family = AF_INET;

    pRecvAddr->sin_port = htons(Port);

    pRecvAddr->sin_addr.s_addr = inet_addr("127.0.0.1");



    ssGetPWork(S)[1] = pSendSocket;

    ssGetPWork(S)[2] = pRecvAddr;

}



void UseFun_SentData(SimStruct *S, const real_T *data, int DataNum)

{

    int iResult;



    char SendBuf[1024]={'\0'};

    int BufLen = 1024;



    SOCKET *pSendSocket    = static_cast<SOCKET *>(ssGetPWork(S)[1]);

    sockaddr_in *pRecvAddr = static_cast<sockaddr_in *>(ssGetPWork(S)[2]);



    if (*pSendSocket == SOCKET_ERROR) {

        printf("SOCKET_ERROR error: %d\n", WSAGetLastError());

        closesocket(*pSendSocket);

        WSACleanup();

        return ;

    }



    //---------------------------------------------

    // Send a datagram to the receiver

    //printf("Sending a datagram to the receiver...\n");

    int ValidateBufLen = 0;

    for(int i=0;i<DataNum;i++)

    {

        ValidateBufLen = strlen(SendBuf);

        sprintf(SendBuf+ValidateBufLen, "%g;", data[i]);

    }



    iResult = sendto(*pSendSocket,

        SendBuf, BufLen, 0, (SOCKADDR *)pRecvAddr, sizeof(sockaddr_in));

}



void UseFun_CloseSock(SimStruct *S)

{

    SOCKET *pSendSocket    = static_cast<SOCKET *>(ssGetPWork(S)[1]);

    sockaddr_in *pRecvAddr = static_cast<sockaddr_in *>(ssGetPWork(S)[2]);



    //---------------------------------------------

    // When the application is finished sending, close the socket.

    printf("Finished socket communication, Closing socket.\n");



    if (closesocket(*pSendSocket) == SOCKET_ERROR)

    {

        printf("closesocket failed with error: %d\n", WSAGetLastError());

    }

    //---------------------------------------------

    // Clean up and quit.



    WSACleanup();



    delete pSendSocket;

    pSendSocket = NULL;



    delete pRecvAddr;

    pRecvAddr = NULL;

}



// Required S-function trailer

#ifdef  MATLAB_MEX_FILE    /* Is this file being compiled as a MEX-file? */

#include "simulink.c"      /* MEX-file interface mechanism */

#else

#include "cg_sfun.h"       /* Code generation registration function */

#endif

 

运行效果图:

S-Function实现simulink仿真与VC通信

 

2.2  数据接收

通过辅助程序,收到上面自定义模型发出来的数据如下

S-Function实现simulink仿真与VC通信

说明:分号前为第一个正弦输入信号的数据,分号后为第二个正弦输入信号的数据。

VC2005 控制台程序代码如下:

SocketServer.cpp

// SocketServer.cpp : 定义控制台应用程序的入口点。

//



#include "stdafx.h"



#ifndef UNICODE

#define UNICODE

#endif



#define WIN32_LEAN_AND_MEAN



#include <winsock2.h>

#include <stdio.h>





// Link with ws2_32.lib

#pragma comment(lib, "Ws2_32.lib")





int _tmain(int argc, _TCHAR* argv[])

{

    int iResult = 0;



    WSADATA wsaData;



    SOCKET RecvSocket;

    sockaddr_in RecvAddr;



    unsigned short Port = 27015;



    char RecvBuf[1024];

    int BufLen = 1024;



    sockaddr_in SenderAddr;

    int SenderAddrSize = sizeof (SenderAddr);



    //-----------------------------------------------

    // Initialize Winsock

    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != NO_ERROR) {

        wprintf(L"WSAStartup failed with error %d\n", iResult);

        return 1;

    }

    //-----------------------------------------------

    // Create a receiver socket to receive datagrams

    RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (RecvSocket == INVALID_SOCKET) {

        wprintf(L"socket failed with error %d\n", WSAGetLastError());

        return 1;

    }

    //-----------------------------------------------

    // Bind the socket to any address and the specified port.

    RecvAddr.sin_family = AF_INET;

    RecvAddr.sin_port = htons(Port);

    RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);



    iResult = bind(RecvSocket, (SOCKADDR *) & RecvAddr, sizeof (RecvAddr));

    if (iResult != 0) {

        wprintf(L"bind failed with error %d\n", WSAGetLastError());

        return 1;

    }

    //-----------------------------------------------

    // Call the recvfrom function to receive datagrams

    // on the bound socket.

    wprintf(L"Receiving datagrams...\n");

    iResult = 0;

    int RecvNum = 0;

    while(RecvNum < 100)

    {

        memset(RecvBuf,0,BufLen);

        iResult = recvfrom(RecvSocket,

            RecvBuf, BufLen, 0, (SOCKADDR *) & SenderAddr, &SenderAddrSize);



        if (iResult == SOCKET_ERROR)

        {

            wprintf(L"recvfrom failed with error %d\n", WSAGetLastError());

            break;

        }

        printf("recv dada: %s \n", RecvBuf);

        RecvNum++;

    }



    //-----------------------------------------------

    // Close the socket when finished receiving datagrams

    wprintf(L"Finished receiving. Closing socket.\n");

    iResult = closesocket(RecvSocket);

    if (iResult == SOCKET_ERROR) {

        wprintf(L"closesocket failed with error %d\n", WSAGetLastError());

        return 1;

    }



    //-----------------------------------------------

    // Clean up and exit.

    wprintf(L"Exiting.\n");

    WSACleanup();



    return 0;

}

 

你可能感兴趣的:(function)