ToFile from SImulink read into C++ standalone program

Looking for sample C or C++ code that will successfully extract MATFILE data created by Simulink's "To File" block. It is time series data, apparently. The sample reader program, matdgns.c, does identify that a single variable, called "ans", is in the MATFILE but I am unable to get the values out into an ordinary C array. "mxGetNumberOfDimensions" indicates "2" which suggests that data is present. Also, the file is 193K bytes, which also indicates that there is data present. However, mxIsEmpty(arr) is returning TRUE.

Answers (4)

Time series objects are classdef OOP objects. To get at the underlying data in them, you will need to use the mxGetProperty API function. This, unfortunately, will produce a deep data copy. If your data is large you might want to explore using the mxGetPropertyPtr function from the FEX. E.g.,
/* Echo timeseries Time and Data values to the screen */
#include "mex.h"
#include <string.h>
int mxIsTimeSeries(const mxArray *mx)
{
int result = 0;
if( mx ) {
result = strcmp(mxGetClassName(mx),"timeseries") == 0;
}
return result;
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mxArray *Time, *Data;
double *t, *d;
int i, n;
if( nrhs && mxIsTimeSeries(prhs[0]) ) {
Time = mxGetProperty(prhs[0],0,"Time"); /* makes deep data copy */
if( Time ) {
t = mxGetPr(Time);
n = mxGetNumberOfElements(Time);
for( i=0; i<n; i++ ) {
mexPrintf("t[%d] = %f\n",i,t[i]);
}
mxDestroyArray(Time);
}
Data = mxGetProperty(prhs[0],0,"Data"); /* makes deep data copy */
if( Data ) {
d = mxGetPr(Data);
n = mxGetNumberOfElements(Data);
for( i=0; i<n; i++ ) {
mexPrintf("d[%d] = %f\n",i,d[i]);
}
mxDestroyArray(Data);
}
}
}
* EDIT *
Try this for standalone code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "matrix.h"
#include "mat.h"
int mxIsTimeSeries(const mxArray *mx)
{
int result = 0;
if( mx ) {
result = strcmp(mxGetClassName(mx),"timeseries") == 0;
}
return result;
}
int main(int argc, char **argv)
{
MATFile *pmat;
int i, n;
mxArray *pa, *Time, *Data;
double *t, *d;
/* If no input argument, do nothing */
if (argc == 0 ) return 0;
/* Open the mat file */
pmat = matOpen(argv[0], "r");
if (pmat == NULL) {
printf("Error opening file %s\n", argv[0]);
return 1;
}
/* Read the desired variable */
pa = matGetVariable(pmat,"ans");
if (pa == NULL) {
printf("Error reading variable 'ans'\n");
matClose(pmat);
return 1;
}
/* Check to see if it is a timseries object */
if( !mxIsTimeSeries(pa) ) {
printf("Error variable 'ans' is not a timeseries object\n");
mxDestroyArray(pa);
matClose(pmat);
return 1;
}
/* Process the data */
Time = mxGetProperty(pa,0,"Time");
if( Time ) {
t = mxGetPr(Time);
n = mxGetNumberOfElements(Time);
for( i=0; i<n; i++ ) {
printf("t[%d] = %f\n",i,t[i]);
}
mxDestroyArray(Time);
} else {
printf("Error variable 'ans' does not have a 'Time' property\n");
}
Data = mxGetProperty(pa,0,"Data");
if( Data ) {
d = mxGetPr(Data);
n = mxGetNumberOfElements(Data);
for( i=0; i<n; i++ ) {
printf("d[%d] = %f\n",i,d[i]);
}
mxDestroyArray(Data);
} else {
printf("Error variable 'ans' does not have a 'Data' property\n");
}
/* Done */
mxDestroyArray(pa);
if (matClose(pmat) != 0) {
printf("Error closing file %s\n",argv[0]);
return 1;
}
printf("Done\n");
return 0;
}

7 Comments

Do I call "mexFunction" from my C++ code? If so, where do I get the parameters (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) to the call?
What is currently available to me is what is provided in the matdgns.c example. There are just the various matXXXX calls.
My example code is for a mex function. For the matdgns.c example, that is a standalone program with main() function so no you would not be using the mexFunction interface. You would use the body of my mexFunction but you would use pa instead of prhs[0]. Unfortunately, for standalone code you cannot use the mxGetPropertyPtr function from the FEX since that only works for mex functions ... you may be stuck with the mxGetProperty API function.
Sorry to be a bother, but since I've already spent 3 fruitless days on this, would it be possible for you to show me complete stand-alone set of code that opens a MATFILE as created by FileTo, with a single variable "ans" in there. The data sent to FileTo is statistics out of SimEvents Servers, so they are all either integers or perhaps doubles (I dont know which). The only input should be a filename and the result should be a one-dimensional C or C++ array of values. I would greatly appreciate it.
I can provide you with an example data file, if it would be helpful.
A small data file would help, yes.
I think the simplest thing for you to do is to use the matdgns.c file as the starting point for your code. Use everything in the code except the "Diagnose array pa" section. Replace that code with this:
mxArray *Time, *Data;
double *t, *d;
int n;
:
/*
* Diagnose array pa
*/
Time = mxGetProperty(pa,0,"Time");
t = mxGetPr(Time);
Data = mxGetProperty(pa,0,"Data");
d = mxGetPr(Data);
n = mxGetNumberOfElements(Time);
:
/* insert code here to use t and d vector data */
:
mxDestroyArray(Data);
mxDestroyArray(Time);
mxDestroyArray(pa);
Time and Data are coming back from mxGetProperty as NULL. Can you load the file I sent and see what named properties are actually present?

Sign in to comment.

Where can I copy or email the sample file? By the way, I will be on travel Friday and Monday, so if I go silent, that is the reason. It won't be because I've lost interest in pursuing this.
File attached.

2 Comments

Time and Data are coming back from mxGetProperty as NULL. Can you load the file I sent and see what named properties are actually present?
I loaded the mat file into MATLAB and saw data there, but have not put together a C program to read it. I can do that tomorrow.

Sign in to comment.

OK, thanks very much. As I mentioned earlier, I will be on business travel Friday and Monday, returning Tuesday.

21 Comments

With the new stand-alone code example, both "Time" and "Data" are still coming back NULL. Also, the two references to argv[0] should both be argv[1] in order to pick up the file name of the MATFILE rather than the name of the executable.
Are you able to get the example code to run on the example file that I provided?
"With the new stand-alone code example, both "Time" and "Data" are still coming back NULL."
Strange. I used this exact code (albeit called from a mex routine) and got the data printed to the screen using your provided mat file.
"... Also, the two references to argv[0] should both be argv[1] …"
My bad … again the result of me calling it directly from a mex routine (calling main) and passing in the filename as the first argv without thinking.
Lemme post the mex routine tomorrow to see if that works for you and then we can go from there to figure out how to get the standalone code to work. The mex code is a very simple wrapper that has a hard-coded filename that it passes to main(), so pretty trivial to write.
So that confirms that the data file is good and the two sets of information (Time and Data) are in there as they are supposed to be. What may be the source of the issue is making the mxGetPtr(Time) and mxGetPtr(Data) calls in a stand-alone C/C++ program. Do you agree?
Here is the mex routine I used to read your mat file, and following that is a sample run I made. Everything seems to work OK with this setup. See if it works for you. The frontend mexFunction is just a very simple wrapper to call main().
/* bare bones limited checking */
#include "mex.h"
int main(int argc, char **argv);
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
char *matname[2] = {"mexFunction","COM2A_To_File_In.mat"};
main(2,matname);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "matrix.h"
#include "mat.h"
int mxIsTimeSeries(const mxArray *mx)
{
int result = 0;
if( mx ) {
result = strcmp(mxGetClassName(mx),"timeseries") == 0;
}
return result;
}
int main(int argc, char **argv)
{
MATFile *pmat;
int i, n;
mxArray *pa, *Time, *Data;
double *t, *d;
/* If no filename input argument, do nothing */
if (argc < 2 ) return 0;
/* Open the mat file */
pmat = matOpen(argv[1], "r");
if (pmat == NULL) {
printf("Error opening file %s\n", argv[1]);
return 1;
}
/* Read the desired variable */
pa = matGetVariable(pmat,"ans");
if (pa == NULL) {
printf("Error reading variable 'ans'\n");
matClose(pmat);
return 1;
}
/* Check to see if it is a timseries object */
if( !mxIsTimeSeries(pa) ) {
printf("Error variable 'ans' is not a timeseries object\n");
mxDestroyArray(pa);
matClose(pmat);
return 1;
}
/* Process the data */
Time = mxGetProperty(pa,0,"Time");
if( Time ) {
t = mxGetPr(Time);
n = mxGetNumberOfElements(Time);
for( i=0; i<n; i++ ) {
printf("t[%d] = %f\n",i,t[i]);
}
mxDestroyArray(Time);
} else {
printf("Error variable 'ans' does not have a 'Time' property\n");
}
Data = mxGetProperty(pa,0,"Data");
if( Data ) {
d = mxGetPr(Data);
n = mxGetNumberOfElements(Data);
for( i=0; i<n; i++ ) {
printf("d[%d] = %f\n",i,d[i]);
}
mxDestroyArray(Data);
} else {
printf("Error variable 'ans' does not have a 'Data' property\n");
}
/* Done */
mxDestroyArray(pa);
if (matClose(pmat) != 0) {
printf("Error closing file %s\n",argv[0]);
return 1;
}
printf("Done\n");
return 0;
}
And the sample run:
>> mex read_timeseries_main.c
>> read_timeseries_main
t[0] = 0.000000
t[1] = 1.000000
t[2] = 1.000000
t[3] = 2.000000
t[4] = 2.000000
t[5] = 3.000000
t[6] = 3.000000
t[7] = 4.000000
t[8] = 4.000000
t[9] = 5.000000
t[10] = 5.000000
t[11] = 6.000000
t[12] = 6.000000
t[13] = 7.000000
t[14] = 7.000000
t[15] = 8.000000
t[16] = 8.000000
t[17] = 9.000000
t[18] = 9.000000
t[19] = 10.000000
t[20] = 10.000000
d[0] = 1.000000
d[1] = 2.000000
d[2] = 1.000000
d[3] = 2.000000
d[4] = 1.000000
d[5] = 2.000000
d[6] = 1.000000
d[7] = 2.000000
d[8] = 1.000000
d[9] = 2.000000
d[10] = 1.000000
d[11] = 2.000000
d[12] = 1.000000
d[13] = 2.000000
d[14] = 1.000000
d[15] = 2.000000
d[16] = 1.000000
d[17] = 2.000000
d[18] = 1.000000
d[19] = 2.000000
d[20] = 1.000000
Done
>> ts = load('COM2A_To_File_In');
>> ts.ans.Time
ans =
0
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
>> ts.ans.Data
ans =
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
I see that yours does work, but mine still does not. Here is my code. What is supposed to call mexFunction? Remember that my application must work where MatLab is not installed. When I included mex.h, suddenly there were unsatisfied externals such as mexPrintf so I removed the mex.h file and it compiled OK (but I still don't see where mexFunction is ever called.
This is a mex function, not a standalone program. The entry point to the mex function is the function mexFunction ... i.e. that is what MATLAB will call upon entry to this dll. In this setup, main() is just another arbitrary function ... it is not the entry point. We are first just trying to easily get code to read your mat file, so compile this as a mex routine and call it from the MATLAB command line. I would like to see this work first before we move on to your standalone program. So do this at the MATLAB command prompt and see what happens:
mex read_timeseries_main.c
read_timeseries_main
I get the same successful result as you do!
But when you pull the wrapper out and just do the main() stuff as a standalone it doesn't read the file properly? How do you build the standalone?
The stand-alone version is built with Visual Studio 2013 using standard build commands for a "console application" (meaning that it has no human interface other than STDIN and STDOUT which are realized as the command line input and output. Let me try again real quick using VS to see what happens....I'll be right back.
OK, here is the latest. I had to make one minor change to get it to compile, which was to comment out the redefintion of printf to mexPrintf in mex.h (I know: boo on me for changing an include file, but it is only a local copy, not the real one from the MATLAB installation.). Once I did that, the VS2013 build worked and the program would run. However, the pointers to Time and Data arrays are still coming back as NULL. But we are closer: now a VS build compiles and runs, but unfortunately maybe it needs something that function mexFunction provides, and that function is no longer being called because "main" is now the entry point under this build. I could call mexFunction from main and have main call the renamed real program - just to get mexFunction into the action. Do you think thatis worth trying? maybe I will so stand bye for that result.
No joy. I guess maybe because nobody is calling mexFunction in the VS2013 stand-alone build, something that the caller of mexFunction would provide is missing.
Can you think of anything that would allow the variables to be identified correctly (as they are) but not allow them to provide pointers to their data?
Nobody should be calling mexFunction in your standalone because that code shouldn't even be there in your standalone! Everything above #include stdio.h in my posted code should be deleted from your standalone source code. In particular, you should not be including mex.h in your standalone and you shouldn't need some hacked up version of it ... mex.h is only for mex routines that have mexFunction etc for calling from MATLAB directly. You also shouldn't be linking in libmex.lib.
I agree with everything that you just said. But the mystery remains: why do Time and Data come back NULL?
You are compiling only the stuff from #include stdio.h and down, and only linking in libmx.lib and libmat.lib? And you are trying to read the exact mat file you posted here? File opens and 'ans' passes the timeseries class test, but no Time or Data properties present?
Yes, I did (among other things) just as you said. Here is a summary:
1. Building with mex: required mexFunction to be defined but never calls it. Successfully dumps the data.
2. Build with VS2013 (preferred): required mex.h included and printf NOT be redefined into mexPrintf (in mex.h). Requires mexFunction NOT be present, else Linker "undefined symbol" occurs during link.
Current code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "matrix.h"
#include "mat.h"
int mxIsTimeSeries(const mxArray *mx)
{
int result = 0;
if( mx ) {
result = strcmp(mxGetClassName(mx),"timeseries") == 0;
}
return result;
}
int main(int argc, char **argv)
{
MATFile *pmat;
int i, n;
mxArray *pa, *Time, *Data;
double *t, *d;
/* If no filename input argument, do nothing */
if (argc < 2 ) return 0;
/* Open the mat file */
pmat = matOpen(argv[1], "r");
if (pmat == NULL) {
printf("Error opening file %s\n", argv[1]);
return 1;
}
/* Read the desired variable */
pa = matGetVariable(pmat,"ans");
if (pa == NULL) {
printf("Error reading variable 'ans'\n");
matClose(pmat);
return 1;
}
/* Check to see if it is a timseries object */
if( !mxIsTimeSeries(pa) ) {
printf("Error variable 'ans' is not a timeseries object\n");
mxDestroyArray(pa);
matClose(pmat);
return 1;
}
/* Process the data */
Time = mxGetProperty(pa,0,"Time");
if( Time ) {
t = mxGetPr(Time);
n = mxGetNumberOfElements(Time);
for( i=0; i<n; i++ ) {
printf("t[%d] = %f\n",i,t[i]);
}
mxDestroyArray(Time);
} else {
printf("Error variable 'ans' does not have a 'Time' property\n");
}
Data = mxGetProperty(pa,0,"Data");
if( Data ) {
d = mxGetPr(Data);
n = mxGetNumberOfElements(Data);
for( i=0; i<n; i++ ) {
printf("d[%d] = %f\n",i,d[i]);
}
mxDestroyArray(Data);
} else {
printf("Error variable 'ans' does not have a 'Data' property\n");
}
/* Done */
mxDestroyArray(pa);
if (matClose(pmat) != 0) {
printf("Error closing file %s\n",argv[0]);
return 1;
}
printf("Done\n");
return 0;
}
Summary for mexFile:
1. required for mex compile
2. disallowed for VS2013 link (else unsat external)
Question: should I try to add whatever dll contains mexFunction into the VS2013 project in order to overcome #2 above?
I happen to be in Phoenix, AZ. Where are you located?
I don't understand either of your 1. and 2. comments above. Mex routines certainly do call mexFunction since that is the entry point for all mex routines called from MATLAB. And standalone programs should not need mex.h included since that stuff is only for the functions that call back into MATLAB which standalone programs don't do.
I need to think about this overnight ...
Any new progress on a stand-alone reader?
Are you still there and thinking about my issue? IT has been a while since I've heard from you.

Sign in to comment.

Categories

Products

Release

R2017b

Tags

Asked:

on 9 Aug 2018

Commented:

on 20 Aug 2018

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!