📄 ext_comm.c
字号:
/*
* Copyright 1994-2001 The MathWorks, Inc.
*
* File: ext_comm.c $Revision: 1.26 $
*
* Abstract:
* Host-side, transport-independent external-mode functions. Calls to these
* functions originate from Simulink and are dispatched through ext_main.c
* Functions are included to:
* o set (send) messages to the target
* o get (receive) messages from the target
* o get (receive) upload data (signals) from the target
* o open connection with target
* o close connection with target
* o etc
*
* Transport specific code (e.g., TCPIP code) resides in ext_transport.c.
* Modify that file to customize external mode to various transport
* mechanisms (e.g., shared memory, serial line, etc).
*/
/*****************
* Include files *
*****************/
/*ANSI C headers*/
#include <stdio.h>
#include <string.h>
#include <windows.h> /* PurgeComm */
/*Real Time Workshop headers*/
#include "tmwtypes.h"
#include "mex.h"
#include "extsim.h"
#include "ext_convert.h"
#include "extutil.h"
#include "ext_transport232.h"
#include "ext_share.h"
/* set verbosity of debugging messages (0, 1, 2, 3 or 4 -> set this to 0 in the release version) */
#define DEBUG_MSG_LVL 0
// ----------------------------------------------------------------------------------------------------
// global variables
// ----------------------------------------------------------------------------------------------------
// maximum number of user data communication channels
#define MAX_UCOM_CHANNELS 10 /* maximum number of user communication channels */
#define MAX_BUF_SIZE 100 /* currently limited to 250 data bytes (FW-12-02) */
// global admin variables of the instance-local communication buffers (dynamically allocated in S-Function 'S0_txd')
typedef struct myUsrBuf_tag {
int_T buffer_full;
uint_T access_count;
uint_T buf_size;
uint8_T *buf;
} myUsrBuf;
myUsrBuf *userTelBuf[MAX_UCOM_CHANNELS]; /* pointer to the data buffer admin structures of all user telegrams */
int_T user_txd_queue[MAX_UCOM_CHANNELS]; /* queue of txd channels to be scanned by Process_UserDownload() */
int_T num_user_channels_active; /* number of active user data channels to be serviced */
int_T next_user_channel_index; /* index of the next channel to be scanned for (txd queue) */
int_T user_comm_enable = 0; /* global flag controlling user downloads (Process_UserDownload) */
// counter of unsuccessful data upload attempts
#ifdef MODELSTUCK
uint_T NoDataPending;
uint_T alarmCounter;
uint_T ModelIsRunning;
#endif
/* keep requesting data every 10 idle transmission loops */
static unsigned int noRequestDataCounter = 0;
// ----------------------------------------------------------------------------------------------------
// local methods
// ----------------------------------------------------------------------------------------------------
/* Function: Init_ComVars ===============================================
* Abstract:
* Initialise the communication variables (buffer pointers, buffer flags) --
* these variables are defined as globals in 's0_usertel_rxd.c'; run-time addresses
* can be established via the workspace variable BufferAdminVar.
* Variable BufferAdminVar is created and initialised in module s0_usertel_rxd,
*/
PRIVATE void Init_ComVars(void) {
mxArray *BufferAdminVar;
uint32_T *BufPtrArray;
uint_T i;
/* block diagram analysis : find out if there are any user data download blocks */
{
mxArray *TxDc;
uint_T n, channel;
real_T *myChannels;
/* analyse 'external' mode block diagram -- ensure that there's only ever one at any one time... */
mexEvalString("myBlocks = find_system(find_system('SimulationMode','external'), 'LookUnderMasks', 'all');");
mexEvalString("TxD = [];for(i = 1:length(myBlocks)), TxD = [TxD ~isempty(findstr(myBlocks{i},'RECEIVE_USR_DATA'))]; end, TxD = find(TxD);");
mexEvalString("TxDc = [];for(i = 1:length(TxD)), TxDc = [TxDc str2num(get_param(get_param(myBlocks{TxD(i)}, 'Parent'), 'channel'))]; end");
/* evaluate variable 'TxDc' -- this variable controls the download to the target (host-TxD) */
if((TxDc = mexGetVariable("caller", "TxDc")) == NULL) {
mexErrMsgTxt("Variable TxDc not found.\n");
}
/* found TxDc -> evaluate */
n = mxGetN(TxDc);
myChannels = mxGetPr(TxDc);
/* preset user_txd_queue (user_txd_queue: list of channels to be scanned for user data download) */
memset(user_txd_queue, 0, MAX_UCOM_CHANNELS);
/* initialise the txd_queue and set the correct buffer size for every TxD channel that is actually used */
for(i=0; i<n; i++) {
channel = (uint_T)myChannels[i];
/* store channel in the list of channels to be serviced (user_txd_queue) */
user_txd_queue[i] = channel;
}
/* set number of upload channels to be serviced */
num_user_channels_active = n;
/* select index of the first channel to be serviced */
next_user_channel_index = 0;
/* remove variables from caller workspace */
mexEvalString("clear myBlocks TxD TxDc i;");
} /* context {block diagram analysis} */
/* communicate the addresses of the buffer pointer array as well as the flag array (via the workspace) */
if((BufferAdminVar = mexGetVariable("base", "BufferAdminVar")) != NULL) {
/* BufferAdminVar exists -> copy the pointers (even if they are all NULL...) */
BufPtrArray = (uint32_T *)mxGetPr(BufferAdminVar);
/* set pointers to all TxD buffers and their respective buffer flags */
for(i=0; i<MAX_UCOM_CHANNELS; i++) {
/* initialise array of global buffer pointers with the values found in the workspace variable */
userTelBuf[i] = (myUsrBuf *)BufPtrArray[i];
#if DEBUG_MSG_LVL >= 2
mexPrintf("ext_comm: Initialised communication channel %d.\n", i);
mexPrintf("ext_comm: Using buffer pointer %ld.\n", userTelBuf[i]);
#endif
}
} /* if(BufferAdminVar...) */
} /* end Init_ComVars */
/* Function: Process_UserDownload ===============================================
* Abstract:
* Find out if a download of user data to the target is pending, initiate download.
*/
PRIVATE void Process_UserDownload(ExternalSim *ES) {
/* get number of the next channel to be downloaded */
int_T req_channel = user_txd_queue[next_user_channel_index++];
/* reset next_user_channel_index if required */
if(next_user_channel_index == num_user_channels_active) next_user_channel_index = 0;
/* check if the contents of the selected buffer have changed */
if(userTelBuf[req_channel]->buffer_full == 1) {
/* yes */
#if DEBUG_MSG_LVL >= 2
mexPrintf("Process_UserDownload: New data available for channel %d. Initiating download to the target.\n", req_channel);
#endif
/* buffer contents have changed -> initiate download */
{
int nSet;
MsgHeader msgHdr;
int msgSize;
boolean_T error = EXT_NO_ERROR;
uint8_T *buf = userTelBuf[req_channel]->buf;
/* determine the size of the user data telgram (stored in buf[0]) */
/* adjust msgSize to ensure the telegram length is a multiple of 4 bytes */
msgSize = (int)(buf[0] + ((4 - (buf[0] & 0x03)) & 0x03));
#if DEBUG_MSG_LVL >= 2
{
int_T i;
mexPrintf("Process_UserDownload: Downloading %d bytes on channel %d.\n", msgSize, req_channel);
mexPrintf("Process_UserDownload: Using data buffer address %ld.\n", (uint32_T)buf);
mexPrintf("Process_UserDownload: Data ");
for(i=0; i<msgSize; i++)
mexPrintf(" :%d", (uint_T)buf[i]);
mexPrintf(":\n");
}
#endif
/*
* Instruct target to receive parameters.
*/
msgHdr.type = (uint32_T)EXT_RECEIVE_USER_DATA;
msgHdr.size = (uint32_T)(msgSize/esGetHostBytesPerTargetByte(ES));
Copy32BitsToTarget(ES, (char *)&msgHdr, (uint32_T *)&msgHdr, NUM_HDR_ELS);
if (!esIsErrorClear(ES)) goto EXIT_POINT;
error = ExtSetTargetMsg(ES,sizeof(msgHdr),(char *)&msgHdr,&nSet);
if (error || (nSet != sizeof(msgHdr))) {
esSetError(ES,"ExtSetTargetMsg() call failed for ExtSetUserData() .\n"
"Ensure target is still running\n");
goto EXIT_POINT;
}
#if DEBUG_MSG_LVL >= 1
printf("Process_UserDownload: Message header sent type %d size %d\n", msgHdr.type, msgHdr.size);
#endif
/*
* Send user data...
*/
if (msgSize > 0) {
/* reverse order of the first 4 data bytes (size, channel, reserved, reserved) -> 9S12, fw-03-05 */
{
char_T dat;
dat = buf[3];
buf[3] = buf[0];
buf[0] = dat;
dat = buf[2];
buf[2] = buf[1];
buf[1] = dat;
#if DEBUG_MSG_LVL >= 1
mexPrintf("Process_UserDownload: First four bytes re-ordered (9S12)\n");
#endif
}
#if DEBUG_MSG_LVL >= 1
mexPrintf("Process_UserDownload: Checking data type length: %d\n", buf[1]);
#endif
/* reverse order of the remaining data bytes, provided this is of type 'xint8', 'xint16' or 'boolean' ... 9S12, fw-03-05 */
/* data type byte has just been moved from buf[2] to buf[1], the length byte is now in buf[3] */
if(buf[1] < 4) {
char_T dat;
int_T i;
for(i=4; i<msgSize; i+=4) {
#if DEBUG_MSG_LVL >= 1
mexPrintf("i = %d/%d\n", i, msgSize);
#endif
dat = buf[i+3];
buf[i+3] = buf[i+0];
buf[i+0] = dat;
dat = buf[i+2];
buf[i+2] = buf[i+1];
buf[i+1] = dat;
}
#if DEBUG_MSG_LVL >= 1
mexPrintf("Process_UserDownload: Remaining %d data bytes reordered (9S12, data type length < 4)\n", msgSize-4);
#endif
}
error = ExtSetTargetMsg(ES, msgSize, buf, &nSet);
if (error || (nSet != msgSize)) {
mexPrintf("Process_UserDownload: Download data (%d bytes) exceeds current buffer size (%d bytes)\n", msgSize, nSet);
{
DWORD err = GetLastError();
mexPrintf("Error code: %ld\n", err);
}
esSetError(ES, "ExtSetTargetMsg() call failed for ExtSetUserData().\n"
"Ensure target is still running\n");
goto EXIT_POINT;
}
#if DEBUG_MSG_LVL >= 1
mexPrintf("Process_UserDownload: Message data sent (%d bytes)\n", nSet);
#endif
/* restore order of the remaining data bytes, provided this is of type 'xint8', 'xint16' or 'boolean' ... 9S12, fw-03-05 */
/* data type byte is still in buf[1], the length byte is still in buf[3] */
if(buf[1] < 4) {
char_T dat;
int_T i;
for(i=4; i<msgSize; i+=4) {
dat = buf[i+3];
buf[i+3] = buf[i+0];
buf[i+0] = dat;
dat = buf[i+2];
buf[i+2] = buf[i+1];
buf[i+1] = dat;
}
#if DEBUG_MSG_LVL >= 1
mexPrintf("Process_UserDownload: Order of %d data bytes restored (9S12, data type length < 4)\n", msgSize-4);
#endif
}
/* restore order of the first 4 data bytes (size, channel, reserved, reserved) -> 9S12, fw-03-05 */
{
char_T dat;
dat = buf[3];
buf[3] = buf[0];
buf[0] = dat;
dat = buf[2];
buf[2] = buf[1];
buf[1] = dat;
#if DEBUG_MSG_LVL >= 1
mexPrintf("Process_UserDownload: Order of first four bytes restored (9S12)\n");
#endif
}
}
} /* context : ExtSetUserData */
/* clear buffer full flag */
userTelBuf[req_channel]->buffer_full = 0;
#if DEBUG_MSG_LVL >= 2
mexPrintf("Process_UserDownload: userTelBuf[%d]->buffer_full = 0\n", req_channel);
#endif
} /* new data available */
EXIT_POINT:
return;
} /* end Process_UserDownload */
/* Function: RequestDataTelegram ===============================================
* Abstract:
* Trigger upload of one data telegram from the target (sets target flag TXactive)
*/
PRIVATE void RequestDataTelegram(ExternalSim *ES)
{
UserData *userData = (UserData *)esGetUserData(ES);
MsgHeader msgHdr;
int_T nSet;
boolean_T error = EXT_NO_ERROR;
/*
* Send the EXT_DATA_UPLD_NOACK_REQUEST msg to the target. This message triggers
* the upload of exactly one data telegram (host driven flow control, FW-10-02)
*/
#if DEBUG_MSG_LVL >= 2
mexPrintf("RequestDataTelegram: Requesting upload of a data telegram (EXT_DATA_UPLD_NOACK_REQUEST).\n");
#endif
//#if DEBUG_MSG_LVL >= 2
//mexPrintf("RequestDataTelegram: Pre-transmission delay... (EXT_DATA_UPLD_NOACK_REQUEST).\n");
//#endif
//Sleep(10);
msgHdr.size = 0;
msgHdr.type = EXT_DATA_UPLD_NOACK_REQUEST;
Copy32BitsToTarget(ES,(char *)&msgHdr,(uint32_T *)&msgHdr,NUM_HDR_ELS);
if (!esIsErrorClear(ES)) {
esSetError(ES, "Copy32BitsToTarget() call failed while requesting new data telegram\n");
goto EXIT_POINT;
}
error = ExtSetTargetMsg(ES,sizeof(msgHdr),(char *)&msgHdr,&nSet);
if (error || (nSet != sizeof(msgHdr))) {
esSetError(ES, "ExtSetTargetMsg() call failed on EXT_DATA_UPLD_NOACK_REQUEST.\n");
mexPrintf("RequestDataTelegram: (no_ack) ExtSetTargetMsg claims to have sent %d bytes\n", nSet);
goto EXIT_POINT;
}
//#if DEBUG_MSG_LVL >= 2
//mexPrintf("RequestDataTelegram: Post-transmission delay... (EXT_DATA_UPLD_NOACK_REQUEST).\n");
//#endif
//Sleep(10);
EXIT_POINT:
return;
} /* end RequestDataTelegram */
/* Function: FreeAndNullUserData ===============================================
* Abstract:
* Free user data and null it out in the external sim struct.
*/
PRIVATE void FreeAndNullUserData(ExternalSim *ES)
{
#if DEBUG_MSG_LVL >= 2
mexPrintf("FreeAndNullUserData called: IN\n");
#endif
ExtUserDataDestroy(esGetUserData(ES));
esSetUserData(ES, NULL);
#if DEBUG_MSG_LVL >= 2
mexPrintf("FreeAndNullUserData called: OUT\n");
#endif
} /* end FreeAndNullUserData */
// foreward declarations
PRIVATE void ExtConnect(ExternalSim *ES, int_T nrhs, const mxArray *prhs[]);
PRIVATE void ExtDisconnectRequest(ExternalSim *ES, int_T nrhs, const mxArray *prhs[]);
/* Function: ExtRecvIncomingMsg ================================================
* Abstract:
* Check for messages (poll) from target on the 'message port'. If a message
* is pending, set the incoming message pending flag to true and set the
* incoming message type. Otherwise, do nothing.
*/
PRIVATE void ExtRecvIncomingMsg(
ExternalSim *ES,
int_T nrhs,
const mxArray *prhs[])
{
boolean_T pending;
char *bufPtr;
int nGot;
boolean_T error = EXT_NO_ERROR;
char *buf = esGetIncomingMsgDataBuf(ES);
int32_T nBytesNeeded = esGetIncomingMsgDataNBytesNeeded(ES);
int nBytes = esGetIncomingMsgDataNBytesInBuf(ES);
UserData *userData = (UserData *)esGetUserData(ES);
(void)nrhs; /* unused */
(void)prhs; /* unused */
#if DEBUG_MSG_LVL >= 3
mexPrintf("\n----------------------\n");
mexPrintf("ExtRecvIncomingMsg: IN\n");
#endif
/*
* Start recv'ing a message.
*/
if (nBytesNeeded == UNKNOWN_BYTES_NEEDED) {
assert(nBytes == 0);
/* carry out download of user telegrams to the target -- if any */
if(user_comm_enable && num_user_channels_active > 0) {
#if DEBUG_MSG_LVL >= 3
mexPrintf("ExtRecvIncomingMsg: Calling the Process_UserDownload() service hook...\n", (uint_T)userData->TelType);
#endif
Process_UserDownload(ES);
}
/*
* Check for pending telegrams.
*/
#if DEBUG_MSG_LVL >= 3
mexPrintf("ExtRecvIncomingMsg: userData->TelType = %d\n", (uint_T)userData->TelType);
#endif
if(userData->TelType == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -