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

文章目录

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

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

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

1、 在模型中添加S-Function, 编写模块对应的函数代码,编写规范可以参考:MATLAB S函数编写教程(MATLAB2010b)

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

matlab-mex

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

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

s-function代码

1、客户端程序,发送simulink仿真数据
UseFunc.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/* 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

2、客户端程序,接收数据
SocketServer.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// 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;
}

参考文章:
S-Function实现simulink仿真与Vc通信
S-Function实现simulink仿真与VC通信

本文出自 TENNFY博客,转载时请注明出处及相应链接。

本文永久链接: https://www.tennfy.com/2161.html

下一篇文章:

上一篇文章:

2人参与了讨论

  1. 超級efly 说:

    完全看不懂 :cry: 只能支持一下了

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

6 + 6 = ?


您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

返回顶部