MC-Basic C-Interface
Introduction
The softMC offers users an option to incorporate applications written in C or C++ into an MC-Basic task. You can write your algorithm in C/C++, compile it with a GNU compiler, download it into a target program and call the code from a program written in MC-Basic or directly from the command line.
The parameters can be passed by value as well as by reference. This function returns a value that can be processed by an MC-Basic application.
MC-Basic provides a facility to load a user object-file into the RAM of the softMC. This file is relocatable and must include information about global symbols for the softMC to find the C-function. The object module may consist of any number of functions. The only restriction is RAM limit.
Object Files
Object files (extension “.o”), contain compiled C-programs and are stored in the Flash disk. You can SEND object files to the controller, load files into RAM with OLOAD and unload them with OUNLOAD. If OLOAD/OUNLOAD fails for any reason, details are found in the OLOAD.ERR file.
Prototype File
For the MC-Basic language translator to match function parameters, provide a C-function prototype file (PROTO.PRO). There is also an option to wrire prototypes in library files, within the declarations section, instead of using the PROTO.PRO file. It is important to understand that matching between the provided softMC prototype and actual C implementation cannot be tested by the translator. It is your responsibility to keep consistency between the function prototype and C implementation.
NOTE | |
To speedup translation of the prototype file, PROTO.PRO is copied into RAM at startup and at every OLOAD. When PROTO.PRO is modified and sent to the controller, issue an OLOAD or reboot the softMC to refresh the prototypes in the RAM. |
The translator is case-insensitive, while object module loader is case-sensitive, so write the name of a C function in capital letters within the C code. softMC prototypes of C functions and C function calls through the softMC are not case sensitive.
Parameters can be passed “by value” and “by reference”
Few object files may be incrementally linked together.
The general syntax for the softMC prototype of a C-function is:
IMPORT_C <Function_Name> ({AS <type>}, {BYVAL AS {<type>}) {AS <type>}
A parameter passed by value has the BYVAL prefix.
A parameter passed by reference has no prefix.
Prototypes do not include parameter names.
A C function prototype with no parameters is written with empty parentheses:
IMPORT_C <Function_Name> ( ) {AS <type>}
A C function prototype with no returned value (a void function) is written as:
IMPORT_C <Function_Name> ({AS <type>}, {BYVAL AS {<type>})
A C-function accepts any combination of double-, long- , string-, joint- and location-type parameters. The parameters may be passed “by value” or “by reference”.
The returned value may be long, double, string, joint, location or none (for C functions with void returned value).
Examples of prototypes and implementation:
|
|
import_c cFunc_LV As Long | int CFUNC_LV(void); |
import_c cFunc_DV As Double | double CFUNC_DV(void); |
import_c cFunc_SV As String | SYS_STRING* CFUNC_SV(void); |
import_c cFunc_PV As Joint Of XYZR | SYS_POINT* CFUNC_PV(void); |
import_c cFunc_GPV As Generic Location | SYS_POINT* CFUNC_GPV(void); |
import_c cFunc_VV() | void CFUNC_VV(void); |
import_c cFunc_LL(ByVal As Long) As Long | int CFUNC_LL(int L); |
import_c cFunc_DRD(As Double) As Double | double CFUNC_DRD(double* D); |
import_c cFunc_SRS(As String) As String | SYS_STRING* CFUNC_SRS(SYS_STRING** S); |
import_c cFunc_PRP(As Joint Of XY) As Location Of XYZ | SYS_POINT* CFUNC_PRP(SYS_STRING** P); |
import_c cFunc_GPRGP(As Generic Location) As Generic Joint | SYS_POINT* CFUNC_GPRGP(SYS_STRING** P); |
Import_C cFunc_LARD([*] as Long) as Double | double CFUNC_LARD(long *l); |
import_c cFunc_SS(ByVal As String) As String | SYS_STRING* CFUNC_SS(SYS_STRING* S); |
import_c cFunc_PGP(ByVal As Generic Joint) As Location Of XYZ | SYS_POINT* CFUNC_PP(SYS_POINT* P); |
import_c cFunc_GPP(ByVal As Location Of XY) As Generic Joint | SYS_POINT* CFUNC_GPGP(SYS_POINT* P); |
Parameters can be of type long, double, string, joint or location; in any order, up to 32 parameters.
Only one-dimensional arrays are allowed as C-functions arguments.
Example of calling a C-function from MC-Basic:
Dim shared Str2global as string Dim shared Str1global as string Dim shared LongArray[100] as long Dim shared d1 as double program ' Use OLOAD to load the object file before loading the program ' that uses the C-Functions ' /* No Parameters */ ' int CFUNC_LV(void) ?CFUNC_LV() ' double CFUNC_DV(double) ?CFUNC_DRD(1.2345) ' /* returns string */ SYS_STRING* CFUNC_SV(void) ?CFUNC_SV() ' /* No Parameters, not return value */ ' void CFUNC_VV(void) CFUNC_VV() ' /* Strings */ ' SYS_STRING* CFUNC_SS(SYS_STRING* S) Str2global=CFUNC_SS(Str1global) ' /* Arrays */ ' double CFUNC_LARD(long *l); d1= cFunc_LARD(LongArray) end program
Example of PROTO.PRO:
'No Parametrs import_c CFUNC_LV() As Long import_c CFUNC_DV() As Double import_c CFUNC_SV() As String import_c CFUNC_PV() As Location Of XYZ import_c CFUNC_GPV() As Generic Joint import_c CFUNC_VV() 'A Single "By Value" Parameter import_c CFUNC_LL(ByVal As Long) As Long import_c CFUNC_DD(ByVal As Double) As Double import_c CFUNC_SS(ByVal As String) As String import_c CFUNC_PP(ByVal As Joint of XY) As Location Of XYZ import_c CFUNC_GPP(ByVal As Generic Location) As Generic Joint 'A Single "By Reference" Parameter import_c CFUNC_LRL(As Long) As Long import_c CFUNC_DRD(As Double) As Double import_c CFUNC_SRS(As String) As String import_c CFUNC_PRP(As Joint of XY) As Location Of XYZ import_c CFUNC_GPRP(As Generic Location) As Generic Joint 'Multiple "By Value" Parameters import_c CFUNC_LVALP(ByVal As double, ByVal As Long, ByVal As String) As Long import_c CFUNC_VVALP(ByVal As String, ByVal As double, ByVal AS Long) 'Multiple "By Reference" Parameters import_c CFUNC_DREFP(As String, As Long, As Double) As Double import_c CFUNC_VREFP(As Long, As Double, As String) ' Mixed Parameters import_c CFUNC_VMIXP(ByVal As Long,As Double,ByVal As String,As String,As Long,ByVal As double) AS Long
Example of test.c:
/* No Parameters */ int CFUNC_LV(void) { return 1; } double CFUNC_DV(void) { return 2.2; } char* CFUNC_SV(void) { return "SV"; } void CFUNC_VV(void) { int fd = open("/tyCo/1",2,0); fdprintf(fd,"VV\r\n"); close(fd); } /* A Single "By Value" Parameter */ int CFUNC_LL(int L) { L = L + 1; return L; } double CFUNC_DD(double D) { D = D + 2.2; return D; } SYS_STRING* CFUNC_SS(SYS_STRING* S) { strcpy(S,"SS"); return S; } /* A Single "By Reference" Parameter */ int CFUNC_LRL(int* L) { *L = *L + 3; return *L; } double CFUNC_DRD(double* D) { *D = *D + 4.4; return *D; } SYS_STRING* CFUNC_SRS(SYS_STRING** S) { strcpy(*S,"SRS"); return *S; } /* Multiple Parameters */ /* By Value Parameters */ int CFUNC_LVALP(double D, int L, SYS_STRING* S) return (D + L + atoi(S)); } void CFUNC_VVALP(SYS_STRING* S, double D, int L) { int fd = open("/tyCo/1",2,0); fdprintf(fd,"Original Values:%d, %f, %s\r\n", L, D, S); L = 10; D = 22.2; strcpy(S,"VValP"); fdprintf(fd,"New Value:%d, %f, %s\r\n", L, D, S); close(fd); } /* By Reference Parameters */ double CFUNC_DREFP(SYS_STRING** S, int* L, double* D) { return (*D + *L + atof(*S)); }
Special Considerations
Motion and System properties can only be passed by value.
KILLTASK will not unblock a task locked inside the C-function. An endless block on a semaphore, message queue, etc. inside a C-function prevent sa task from correct being killed. If C-function has some blocking operation system call (e.g., puting a task to sleep, taking a semaphore, passing messages through a message queue, IO operation, etc.), this may interfere with killing the user task.
When an object module is loaded several times without unloading, all its instances are kept in the RAM, while the system symbol table has references only to the resent instance. MC-Basic applications may refer to the obsolete version of some modified object file.
OUNLOAD does not check to see if the object file is in use. It is your responsibility to ensure that the object is not unloaded as long as there tasks and libraries that refer to the object.
Consider following example, both the program TASK1.PRG and TASK2.PRG use the same function from object module.
-->oload my_obj.o -->load TASK1.PRG -->send my_obj.o ‘ send updated version of object file -->oload my_obj.o -->load TASK2.PRG
In this example, TASK1.PRG references the oldest instance of my_obj.o while TASK2.PRG references the recent version.