IP Protect using static/shared library
Contents
Workflow
We support importing Dymola FMU into SimWB. To run the FMU in SimWB, we need binary for Linux 64-bit or 32-bit depending on the SimWB server. Dymola is currently only available in Windows and they are working towards having a Linux version as well. Meanwhile we need to follow the steps below to get a working FMU in SimWB. Following steps assume you are using Dymola 2015.
Steps for Suppliers
The 'Suppliers' do not need to purchase SimWB or custom hardware to make this work. All they have to so is create a static library in say Centos 64-bit system if the SimWB is a 64-bit system. The idea is to have the supplier send two different static libraries for Windows and Linux. Assuming the supplier has a S-Function that he wants to protect, see below for the steps:
Assume if we have the following code in S-Function
/* * File : timesthree.c * Abstract: * An example C-file S-function for multiplying an input by 3, * y = 3*u * */ #define S_FUNCTION_NAME timesthree #define S_FUNCTION_LEVEL 2 #include "simstruc.h" /*================* * Build checking * *================*/ /* Function: mdlInitializeSizes =============================================== * Abstract: * Setup sizes of the various vectors. */ static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams(S, 0); if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch will be reported by Simulink */ } 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); /* specify the sim state compliance to be same as a built-in block */ ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE); ssSetOptions(S, SS_OPTION_WORKS_WITH_CODE_REUSE | SS_OPTION_EXCEPTION_FREE_CODE | SS_OPTION_USE_TLC_WITH_ACCELERATOR); } /* Function: mdlInitializeSampleTimes ========================================= * Abstract: * Specifiy that we inherit our sample time from the driving block. */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); ssSetModelReferenceSampleTimeDefaultInheritance(S); } /* Function: mdlOutputs ======================================================= * Abstract: * y = 2*u */ static void mdlOutputs(SimStruct *S, int_T tid) { int_T i; InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); int_T width = ssGetOutputPortWidth(S,0); for (i=0; i<width; i++) { /* * This example does not implement complex signal handling. * To find out see an example about how to handle complex signal in * S-function, see sdotproduct.c for details. */ *y++ = 2.0 *(*uPtrs[i]); } } /* Function: mdlTerminate ===================================================== * Abstract: * No termination needed, but we are required to have this routine. */ static void mdlTerminate(SimStruct *S) { } #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
Move the S-Function logic to external source file
/* * File : timesthree.c * Abstract: * An example C-file S-function for multiplying an input by 3, * y = 3*u * */ #define S_FUNCTION_NAME timesthree #define S_FUNCTION_LEVEL 2 #include "simstruc.h" #include "externallogic.h" /*================* * Build checking * *================*/ /* Function: mdlInitializeSizes =============================================== * Abstract: * Setup sizes of the various vectors. */ static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams(S, 0); if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch will be reported by Simulink */ } 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); /* specify the sim state compliance to be same as a built-in block */ ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE); ssSetOptions(S, SS_OPTION_WORKS_WITH_CODE_REUSE | SS_OPTION_EXCEPTION_FREE_CODE | SS_OPTION_USE_TLC_WITH_ACCELERATOR); } /* Function: mdlInitializeSampleTimes ========================================= * Abstract: * Specifiy that we inherit our sample time from the driving block. */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); ssSetModelReferenceSampleTimeDefaultInheritance(S); } /* Function: mdlOutputs ======================================================= * Abstract: * y = 2*u */ static void mdlOutputs(SimStruct *S, int_T tid) { int_T i; InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); int_T width = ssGetOutputPortWidth(S,0); compute(y,width); /* The above call to 'compute' is implemented in externallogic.c and it has the following code for (i=0; i<width; i++) { /* * This example does not implement complex signal handling. * To find out see an example about how to handle complex signal in * S-function, see sdotproduct.c for details. */ *y++ = 2.0 *(*uPtrs[i]); } */ } /* Function: mdlTerminate ===================================================== * Abstract: * No termination needed, but we are required to have this routine. */ static void mdlTerminate(SimStruct *S) { } #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
Steps for End User/OEM
- Move the generated FMU to SimWB server (RedHawk) system.
- Unzip the FMU in a temporary folder
unzip FilterPK_0Simple.fmu
- Navigate to the source folder in the unzipped folder
cd sources
- Edit 'fmiModelIdentifier.h' and comment the '#define FMI_MODULE_NAME FilterPK_0Simple.dll' (//#define FMI_MODULE_NAME FilterPK_0Simple.dll)
- Edit the 'all.c' and comment the '#define DYMOLA_STATIC static' (//#define DYMOLA_STATIC static)
- Create a Makefile in the 'sources' directory. The 'MODEL' variable below should be changed to match the 'modelIdentifier' defined in 'modelDescription.xml' in the FMU file:
MODEL=FilterPK_0Simple FMILINSTALLPATH=/usr/local/ccursim/FMIL CFLAGS=-fPIC -g -O2 -DDYM2CCUR -I. -I\$(FMILINSTALLPATH)/include/FMI2 -I\$(FMILINSTALLPATH)/include/FMI -I\$(FMILINSTALLPATH)/include objects = all.o FMU=../binaries/linux64/$(MODEL).so fmulib:$(FMU) $(FMU): $(objects) mkdir -p ../binaries/linux64 ld -shared -o $(FMU) $(objects) $(LINKERUSEROPTIONS) -L/usr/local/ccursim/FMIL/lib64 -lfmilib -lrt -lm %.o:%.c $(CC) -fPIC -c $(CFLAGS) $< clean: rm -f *.o rm -f \$(FMU) zip: fmulib rm -f $(MODEL).fmu cd ../ && zip --exclude temp.fmu -q -r $(MODEL).fmu .
- Now let's generate FMU with Linux binary.
make zip
- Above step will create 'FilterPK_0Simple.fmu' in a folder where 'sources' folder is located. We can use this FMU file in SimWB.
Import FMU to SimWB
- Open the 'FMU Models' pane in SimWB
- Click on Select FMU file' button and navigate to the location of FMU created above.
- Click on 'Parse FMU' button. This will show the input, output, and parameter variables available in FMU to be mapped to RTDB. Inputs and Outputs are selected by default. You can select other variables as required.
- Click on 'New RTDB' button, provide a name for the RTDB to be created, and click 'Generate RTDB'
- Click on the 'Install FMU' button to install the FMU.
- Click on the 'Create FMU Model' to create a FMU model object in SimWB
- Now we are ready to use this object in any test like any other model