Start of trail End of trail navigation bar

Table of Contents > QUARC > User's Guide > QUARC External Interfaces > C Functions > Target Management

Target API

The Target API is used to manage the real-time code on a target using C/C++ based applications. The QUARC Target API allows users to download, run and stop models as well as monitor the standard I/O of a model from their application. This API can also be used to configure the QUARC Target Manager and get information about its configuration.

The Target API contains functions for connecting to a target, downloading models, running and stopping models, uploading files from a target, monitoring models' standard I/O, autoloading models and configuring the QUARC Target Manager. All of these functions are declared in two header files; and .

This section is a guide on how to use the QUARC Target API to manage the real-time codes of models on a target. Before going into details, a set of steps are required to setup your C/C++ application to use the Target API. Please use the following list to refer to each topic:

Setting Up the C/C++ Application to Use the Target API

The user needs to follow a set of steps to setup the C/C++ application to use the Target API. We assume that the Microsoft Visual Studio 2015 or later version is being used to develop the application. Please perform the following procedure to setup your application, once in the Microsoft Visual Studio environment:

  1. Open the Property Pages window by right-clicking on the project containing your application and choosing the Properties menu item in the context menu. The following figure illustrates this window.

    Property Pages Window

  2. In the Property Pages window, click on the C/C++ pane. The first parameter in this pane is called Additional Include Directories. Type in $(QSDK_DIR)include in its field as shown in the figure below. Note that QSDK_DIR is an environment variable referring to the directory in which QUARC has been installed.

    Property Pages Window

  3. Click on the Linker pane and expand its treeview. In the General section of this pane, there is a parameter called Additional Library Directories. For the Win32 platform, type in $(QSDK_DIR)lib\windows in its field as shown in the figure below. For the x64 platform, use $(QSDK_DIR)lib\win64 instead, since the code must be linked with 64-bit versions of the QUARC libraries in that case.

    Property Pages Window

  4. While in the Linker pane, click on the Input section. The first parameter in this section is called Additional Dependencies. Type in quarc_target.lib quanser_communications.lib quanser_runtime.lib quanser_common.lib in its field as illustrated in the following figure. Note that the entries in the field should be separated by one space.

    Property Pages Window

  5. Once all these changes are made, click on Apply and then OK.

Connecting to a Target

The Target API contains a pair of functions to connect/disconnect to/from a target. These two functions are listed below:

We use a C++ application to demonstrate how to use these functions to make a connection to a target and close the connection. This application first connects to a target, stops a currently running model and then closes the connection.

Before going into the details of the source code for our example, remember to include the header files containing the functions used in your code. For the Target API, you need to include the "quarc_target.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include "quanser_messages.h"
#include "quarc_target.h"

The first section of the source code is dedicated to variable definitions and initialization. The following code segment shows this section.

#include "stdafx.h"

int _tmain(intint argc, _TCHAR* argv[])
{
	
	/* The target handle used to establish a connection with the target. */
	t_target client_target;
	
	const char*    target_uri = "shmem://quarc-target:1";
	t_int receive_buffer_size = 1000;
	t_int send_buffer_size = 1000;
	
	/* This variable is used to store the returned value by the functions in the API. */
	t_error result;
	char message[512];

The client_target instance is a handle used to connect to a target. There is a variable called target_uri which refers to the URI used to connect to the target. In fact, this is the URI used by the QUARC Target Manager to listen for new connections. Every time you want to manage models on a target, you need to use the target URI used by the QUARC Target Manager running on that target to connect to it. For more information on QUARC Target Manager and the Target URI, please refer to the QUARC Targets/Communicating with the Target/QUARC Target Manager - the Target URI section. In our example, we have used shmem://quarc-target:1 as the target URI. This URI refers to the QUARC Target Manager running on the same target as the application. The two variables for send/receive buffer sizes are used by the target_connect function to set the send/receive buffer sizes for the connection.

The next section is where the application tries to connect to the QUARC Target Manager using the specified target URI. Upon establishing the connection, a running model is stopped to verify that the connection is indeed established and the connection is then closed. This section of the source code is illustrated by the code segment below.

	/* 
		This function connects to a target using the specified target URI.
		The receive and send buffer sizes are also passed as parameters.
		The target handle declared above is used by this function. Once 
		the target_connect function connects to a target, all the other 
		functions will use this handle to refer to the connected target. 
	*/	
	result = target_connect(target_uri, receive_buffer_size, send_buffer_size, &client_target);
		
	/* Print the appropriate message when the target_connect fails to connect to the target. */
	if ( result < 0)
	{
		msg_get_error_messageA(NULL, result, message, sizeof(message));
		printf("Unable to connect to the target. %s\n", message);
	}	
		
	/* Now connected. */
	if (result == 0)
	{
		/* 
			We want to verify that the application has connected to the target. 
			The function we use here is target_stop_model which stops a loaded, 
			running model on the target.
		*/

		/*
			The model name used here should be changed to the name of the model
			actually running on the target. For example, if the model you have chosen
			to run is called model_1, this variable should be set to "model_1".
		*/
		char * model_name = "example";
		result = target_stop_model(client_target, model_name);
		
		/* Print the appropriate message in case of errors. */
		if (result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message));
			printf("The specified model cannot be stopped. %s\n", message);
		}
	}
	
	/* Close the target handle. */
	target_close(client_target);
	
	getchar();
	return 0;
}

The target_connect function tries to connect to a target using the given target URI. This function attempts to use the given target URI to connect to the QUARC Target Manager running on the target. On successful operation, it returns 1 and it returns a negative error code in case of errors. Once connected to the target, all the other functions will use the client_target handle to refer to the connection. After connecting to the target, we verify that the connection is established by stopping a running model. The function being used is called target_stop_model which uses the target handle and a model's name. Note that the model name specified in our example should be replaced by the actual model running on the target. In other words, you need to run a model on the target you want to connect to, and set the model_name variable to the name of the running model. More information about the target_stop_model function is given in the Running Models section. Once the verification is done, the target handle created is closed by the target_close function.

You can now build your application. Before running the application, make sure that a model is running on the target. You can then run the application. To verify that the application has operated successfully, bring the list of running processes on your system to check if the process of the running model was indeed stopped.

Warning As mentioned in this section, you always need to make a connection to a target before being able to manage models on that target. Therefore, the source code given in this section is included in all the other sections of this documentation as well.

Warning Always make sure that you close the target handle at the end of your application. If you do not close the target handle, the next time you want to connect to the same target using the same target URI will result in errors, since the last connection is still open and the QUARC Target Manager is still trying to serve that connection which is no longer in use.

Downloading Models

The Target API contains 4 functions for downloading models, removing downloaded models and retrieving the list of downloaded models. These functions are listed below:

We use a C++ based application to demonstrate how to use the above mentioned functions to download models on a target, remove downloaded models from a target and get the list of downloaded models on a target. This application first connects to a target using a given target URI, downloads two models on the target, removes one of them and finally retrieves the list of downloaded models.

Before going into the details of the source code for our example, remember to include the header files containing the functions used in your code. For the Target API, you need to include the "quarc_target.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include "quanser_messages.h"
#include "quarc_target.h"

The first section of the source code is where the application attempts to connect to the target using the target URI specified. There are two additional function definitions included in this section which are explained later. You can also refer to the comments for these functions for more details. The following code segment shows this section.

#include "stdafx.h"

/*
	This function is used by the model enumerating functions for printing out the model names.
	For example, target_get_downloaded_models uses this function to print out all the downloaded
	models.
*/
static t_int model_callback(t_target dpc, t_long context, const char * model_name, const char * model_arguments)
{
	printf("Model Name: %s - Model Arguments: %s\n", model_name, model_arguments);
	return 0;
}

/*
	This function is used by the target_download_model_model function to read the opened file and get the
	real-time code to be downloaded to the target.
*/
static t_int download_file_callback(t_target dpc, t_long file_handle, t_int buffer_length, t_byte * buffer)
{
	return _read(static_cast<int>(file_handle), buffer, buffer_length);
}

int _tmain(int argc, _TCHAR* argv[])
{
	
	/* The target handle used to establish a connection with the target. */
	t_target client_target;

	const char *	target_uri = "shmem://quarc-target:1";
	t_int receive_buffer_size = 1000;
	t_int send_buffer_size = 1000;

	/* This variable is used to store the returned value by the functions in the API. */
	t_error result;
	
	char message[512];
	
	/* 
		This function connects to a target using the specified target URI.
		The receive and send buffer sizes are also passed as parameters.
		The target handle declared above is used by this function. Once 
		the target_connect function connects to a target, all the other 
		functions will use this handle to refer to the connected target. 
	*/	
	result = target_connect(target_uri, receive_buffer_size, send_buffer_size, &client_target);
	
	/* Print the appropriate message when the target_connect fails to connect to the target. */
	if ( result < 0)
	{
		msg_get_error_messageA(NULL, result, message, sizeof(message));
		printf("Unable to connect to the target. %s\n", message);
	}	
		
	/* Now connected. */
	if (result == 0)
	{

For a detailed description of the above code segment, you can refer to the Connecting to a Target section. The first function defined in the source code is called model_callback which is used by model enumerating functions to display the list of models. This function accepts the target handle in addition to model name and arguments and prints them out on the console. The next function defined in the code is called download_file_callback which is used by the target_download_model_model to read an opened file and get the real-time code to be downloaded on the target. It accepts the target handle in addition to a file descriptor referring to the opened file, a buffer to store the read data in and the buffer's length. This function calls another function called _read, which is included in the "io.h" header file, to read the opened file.

The next section of our source code is where a model gets downloaded on the target using the target_download_model_model function and is illustrated by the following code segment.

		/* The path to the model's executable file (real-time code). */
		static const char path[] = "C:\\Quanser\\example.exe";	
		
		/* 
			This function opens the file specified by the path argument. We have used
			3 macros to define how the file should be opened. The file should be opened
			in binary (untranslated) format, caching is optimized for sequential access
			from hard disk and the file is opened for reading-only. The returned value 
			is a file descriptor for the opened file. In case of errors a value of -1 is
			returned.
		*/
		int file_handle = _open(path, _O_BINARY | _O_SEQUENTIAL | _O_RDONLY, 0);
		
		/* The _open function returned successfully. */
		if (file_handle != -1)
		{
			
			/* 
				An instant of the download struct. This instant will be used 
				by target_download_model_model function.
			*/
			t_download download_interface;
			
			/* 
				One of the members in the download struct is a pointer to a function (called get_code).
				We set this pointer to point to the download_file_callback function handle to read the
				contents of the file specified by its descriptor.
			*/
			download_interface.get_code = download_file_callback;
			
			/*
				This function downloads the specified real-time code to the specified target. 
				The generator parameter specifies which program has created the real-time code.
				The pointer to the download struct instant created earlier is passed as a parameter
				to this function, so the opened file can be read and then downloaded to the target.
			*/
			result = target_download_model_model(client_target, "example", "simulink", path, &download_interface, file_handle);
			
			/* In case the download function cannot download the opened file, print the appropriate message. */
			if (result < 0)
			{
				msg_get_error_messageA(NULL, result, message, sizeof(message));
				printf("The specified model cannot be downloaded to the target. %s\n", message);
			}
			
			/* Once the download operation is completed, close the opened file. */
			_close(file_handle);
		}

The path of the folder in which the real-time code (model's executable file) resides is given in the path string. Note that you need to change this to correspond to the actual path in which you have saved your executable file. The _open function opens a file located in the given path and is defined in "io.h" header file. If it returned successfully, a file descriptor referring to the opened file is returned. Otherwise, a value of -1 is returned. We have used 3 macros to define how the file should be opened. The file should be opened in binary (untranslated) format, caching is optimized for sequential access from hard disk and the file is opened for reading-only. All these macros are defined in the "fcntl.h" header file. Once the file is opened successfully, we use the target_download_model_model function to download the real-time code on the target we have connected to. But before invoking this function, an instant of the download struct is initialized. This struct contains a pointer to a function called get_code which is set to the download_file_callback function handle. Next in the code is the line where the target_download_model_model function is invoked. This function downloads the specified real-time code to the specified target. The generator parameter specifies which program has created the real-time code. The pointer to the download struct instant created earlier is passed as a parameter to this function, so the opened file can be read and then downloaded to the target. Once the download operation is completed, the opened file is closed using the _close function defined in "io.h". A message is printed out in case the downloading function cannot perform the operation.

Note that the model name passed as a parameter to the target_download_model_model is for informative purposes only and does not affect the way this function performs. The name of the model downloaded on the target will get assigned to this parameter. For instance, if the executable file is called "my_model" and is opened and read for downloading, and you set the model name to "example", the real-time code for "my_model" will still be downloaded on the target. However, the name of this downloaded model will be "example" on the target.

The next section is where a model is downloaded using the target_download_model_model_file function. Next, a downloaded model is removed from the target and finally, the list of downloaded models is retrieved and printed out on the console. The following code segment shows this section.

		const char * path2 = "C:\\Quanser\\example2.exe";
		/* 
			This function is the high-level download function used to download models to the
			target. You do not need to open and read a file before downloading it. All you need
			to do is to specify the path in which the file resides. This function is built upon 
			the target_download_model_model function.
		*/
		result = target_download_model_model_file(client_target, "simulink", path2);
		
		/* 
			This instant of model_enumeration struct is used by functions enumerating 
			models (e.g. target_get_downloaded_models). 
		*/
		t_model_enumeration model_interface;
		
		/*
			One of the members of the model_enumeration struct is a pointer to a function
			(called enumerate_model). We set this pointer to point to the model_callback
			function. this way, when the pointer to this struct is passed into model enumerating
			functions, the model name and arguments are printed out to the console.
		*/
		model_interface.enumerate_model = model_callback;
		
		/* 
			This functions removes the specified downloaded model from the target. 
			If successful, it returns zero. In case of errors, a negative error
			code is returned.
		*/ 
		result = target_remove_downloaded_model(client_target,"example");
		
		/* In case of errors, print the appropriate message. */
		if (result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message));
			printf("The specified model cannot be removed from the target. %s\n", message);
		}
	
		/*
			This function gets the list of downloaded models on the specified target.
			The pointer to the model_enumeration struct instant is passed as a parameter. 
			This way, this function uses the model_callback method to print out the model 
			name and arguments.
		*/
		result = target_get_downloaded_models(client_target, &model_interface, NULL);
		
		/* In case of errors, print the appropriate message. */
		if (result < 0)
		{	
			msg_get_error_messageA(NULL, result, message, sizeof(message));
			printf("The list of downloaded models on the target cannot be retrieved. %s\n", message);
		}

	}
	/* Close the target handle. */
	target_close(client_target);
	getchar();
	return 0;
}

The path2 string contains the path of the executable file to be downloaded on the target. You should change this path to the one in which the actual real-time code resides. The target_download_model_model_file function is the high-level function used to download models to the target. You do not need to open and read a file before downloading it. All you need to do is to specify the file's path. Note that this function is built upon the target_download_model_model function. The target_download_model_model function is mostly used in situations where the source to download from is something other than a file (e.g. an FTP location). In case you always download models from a file, you do not need to use target_download_model_model.

An instant of model_enumeration struct is defined and initialized in the above code segment. This struct contains a pointer to a function called enumerate_model which is set to the model_callback function handle. This way, the model enumerating functions can use this struct's instant to display the list of enumerated models. The target_get_downloaded_models function displays the list of downloaded models with the use of the model_enumeration struct's instant. But before invoking this function, the target_remove_downloaded_model is called to remove the first downloaded model, "example". Once this operation is performed, the list of downloaded models is retrieved. In case any of these functions returned with an error, an appropriate message is displayed. Once all the tasks are completed and there is no more use for the target connection, we close the target handle, hence closing the connection.

You can now build your application. Before running the application, make sure you have placed the real-time codes (executable files) in the right folders and the paths are referring to those folders. Once this step is done, you can run your application. If you look at the list of downloaded models, you will notice that there is only one model downloaded on the target (the second one). Note however that you might see other model names as being downloaded on your target as well. These models are the ones you have run on the specified target. Since stopping the models will not remove the downloaded real-time code, it will be still in the list of downloaded models on the target.

Warning Always make sure that you close the target handle at the end of your application. If you do not close the target handle, the next time you want to connect to the same target using the same target URI will result in errors, since the last connection is still open and the QUARC Target Manager is still trying to serve that connection which is no longer in use.

Running Models

The Target API contains function for running models on a target. These function are listed below:

We use a C++ application to demonstrate how to use the above listed functions to run models on a target. This application first attempts to connect to a target and upon connection, loads two models on the target and then retrieves the list of loaded models. Next, one of the models is stopped and later is switched back in and the other model running is switched out. Finally, the model that got switched in is stopped and the updated list of loaded models is retrieved.

Before going into the details of the source code for our example, remember to include the header files containing the functions used in your code. For the Target API, you need to include the "quarc_target.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include <windows.h>
#include "quanser_messages.h"
#include "quarc_target.h"

The first section of the source code is where the application attempts to connect to the target using the target URI specified. There are two additional function definitions included in this section which are explained later. You can also refer to the comments for these functions for more details. The following code segment shows this section.

#include "stdafx.h"

/*
	This function is used by the model enumerating functions for printing out the model names.
	For example, target_get_loaded_models uses this function to print out all the downloaded
	models.
*/
static t_int model_callback(t_target dpc, t_long context, const char * model_name, const char * model_arguments)
{
	printf("Model Name: %s - Model Arguments: %s\n", model_name, model_arguments);
	return 0;
}

/*
	This function is used to prompt the user to press Enter before
	continuing on with the application. The reason for having this 
	function is to give time for the user to verify the functionality 
	of different functions. For example, after loading a model, the user
	might want to verify that the model is indeed loaded on the target
	by connecting to the model in the Simulink model window.
*/
static t_int waiting_for_user()
{
	printf("Press Enter to continue...\n");
	getchar();
	return 1;
}

int _tmain(int argc, _TCHAR* argv[])
{
	
	/* The target handle used to establish a connection with the target. */
	t_target client_target;

	const char *	target_uri = "shmem://quarc-target:1";
	t_int receive_buffer_size = 1000;
	t_int send_buffer_size = 1000;

	/* This variable is used to store the returned value by the functions in the API. */
	t_error result;
	
	char message[512];
	
	/* 
		This function connects to a target using the specified target URI.
		The receive and send buffer sizes are also passed as parameters.
		The target handle declared above is used by this function. Once the
		target_connect function connects to a target, all the other functions 
		will use this handle to refer to the connected target. 
	*/	
	result = target_connect(target_uri, receive_buffer_size, send_buffer_size, &client_target);
		
	/* Print the appropriate message when the target_connect fails to connect to the target. */
	if ( result < 0)
	{
		msg_get_error_messageA(NULL, result, message, sizeof(message));
		printf("Unable to connect to the target. %s\n", message);
	}	
		
	/* Now connected. */
	if (result == 0)
	{

For a detailed description of the above code segment, you can refer to the Connecting to a Target section. The first function defined in the source code is called model_callback which is used by model enumerating functions to display the list of models. This function accepts the target handle in addition to model name and arguments and prints them out on the console. The next function defined in the code is called waiting_for_user which prompts the user to press Enter and then returns. The reason for having this function is to give time for the user to verify the functionality of different functions.

The next section is where two models get loaded on the target and the list of loaded models is retrieved from the target we have connected to. It is illustrated by the following code segment.

		/* The model names. These should be changed to the user's actual model names. */
		const char * model_1 = "example";
		const char * model_2 = "example2";
		
		/* 
			This instant of model_enumeration struct is used by functions enumerating 
			models (e.g. target_get_loaded_models). 
		*/
		t_model_enumeration model_interface;
		
		/*
			One of the members of the model_enumeration struct is a pointer to a function
			(called enumerate_model). We set this pointer to point to the model_callback
			function. this way, when the pointer to this struct is passed into model enumerating
			functions, the model name and arguments are printed out to the console.
		*/
		model_interface.enumerate_model = model_callback;
		
		/* 
			This function loads the model specified by its name and arguments.
		*/
		result = target_load_model_model(client_target, model_1, "");
		
		/* In case of errors loading the model, print the appropriate message. */
		if (result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to load the model %s. %s\n", model_1, message);
		}
		
		/* 
			This function loads the model specified by its name and arguments.
		*/
		result = target_load_model_model(client_target, model_2 ,"");
		
		/* In case of errors loading the model, print the appropriate message. */
		if (result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to load the model %s. %s\n", model_2, message);
		}
		
		/* The Sleep call gives time for the models to load. */
		Sleep(1000);
		
		printf("The following is the list of loaded models:\n");
		/*
			This function gets the list of loaded models on the specified target.
			The pointer to the model_enumeration struct instant is passed as a parameter. 
			This way, this function uses the model_callback method to print out the model 
			name and arguments.
		*/
		result = target_get_loaded_models(client_target, &model_interface, NULL);
		
		/* Wait for user to press Enter to continue. */
		waiting_for_user();

The model names set here should be replaced with the actual models to be loaded. Remember to download the models before attempting to load them. Otherwise, the application displays an error message complaining that the real-time code for the model does not exist. You can download the models either from the Simulink model window, which is described in QUARC Basics/Basic Procedures/Downloading a Model's Real-Time Code section of the QUARC documentation in MATLAB, or use either of the two functions in the Target API for downloading models which are explained in the Downloading Models section. An instant of model_enumeration struct is defined and initialized in the above code segment. This struct contains a pointer to a function called enumerate_model which is set to the model_callback function handle. This way, the model enumerating functions can use this struct's instant to display the list of enumerated models.

The target_load_model_model function loads a model specified by its name and arguments. This function is invoked twice since we want to load two models. The target_get_loaded_models retrieves the list of loaded models. Before calling this function, the Sleep function, included in the "windows.h" header file is called to give time for the models to load. Once the sleep time expires, the list of loaded models is displayed. The waiting_for_user function prompts the user to press Enter before returning. This allows the user to check if the models are actually loaded or not by connecting to the target from the model's Simulink diagram or checking the list of processes.

In the next section of the code, the second loaded model gets stopped. Afterwards, it is switched in and the first model is switched out. The concept of switching in/out comes from dynamic reconfiguration feature of QUARC which is documented in the Dynamic Reconfiguration section of the QUARC documentation. Finally, the real-time code of the switched model is killed and the updated list of loaded models is displayed. This section of the code is shown by the following code segment.

		/*
			This function sends a STOP signal to the model. When this
			function returns, the model may NOT have stopped yet as it
			may still be executing the model termination code.
		*/
		result = target_stop_model(client_target, model_2);
		
		/* Print the error message in case of errors. */
		if (result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to stop the model %s. %s\n", model_2, message);
		}
		
		/*
			The Sleep function below gives time for model_2 to finish 
			terminating. Without this sleep time, the model_2 may be
			in the process of terminating and getting the list of loaded
			model will show model_2 as well.
		*/
		Sleep(1000);
		
		/*
			This function runs model_2 again, but in the process it
			dynamically switches out any model running in reconfiguration
			group 0 (i.e. model_1). Thus, model_1 will stop running and
			model_2 will start running.
		*/
		result = target_switch_model_to_model(client_target, model_2 , "", 0);
		
		/* Print the error message in case of errors. */
		if(result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to switch to model %s. %s\n", model_2, message);
		}
		
		/*
			The Sleep call below will give time for model_1 to exit. Without
			this call, the list of loaded models may show model_1 as well.
		*/
		Sleep(1000);

		/* 
			We have used the target_is_model_loaded function to check if model_1
			is loaded. This function returns 1 if the model is loaded and returns
			zero if the model is not loaded.
		*/
		if( target_is_model_loaded(client_target, model_1) == 1)
		printf("The model %s is loaded on the target.\n", model_1);
		else 
		printf("The model %s is not loaded on the target.\n", model_1);
		
		waiting_for_user();
		
		/* 
			This function kills the model specified by its name. It is 
			not recommended for the user to use this function. Use 
			target_stop_model instead. Use this function if absolutely 
			necessary. It is included here to demonstrate how to use 
			this function.
		*/
		result = target_kill_model(client_target, model_2);
		
		/*
			The Sleep call below will give time for model_2 to exit. Without
			this call, the list of loaded models may show model_2 as well.
		*/
		Sleep(1000);
		
		/* Print out the list of loaded model now. */
		printf("The following is the updated list of loaded models:\n");
		result = target_get_loaded_models(client_target, &model_interface, NULL);
	}
	/* Close the target handle. */
	target_close(client_target);
	
	
	waiting_for_user();
	return 0;
}

The target_stop_model function sends a STOP signal to the model specified by its name. This action causes the model to terminate (graceful shutdown). The reason for setting a sleep time after stopping the model is that by the time target_stop_model returns, the model might not be terminated yet. Once the sleep time expires, the target_switch_model_to_model function is called. This function runs model_2 again, but in the process, it dynamically switches out any model running in reconfiguration group 0 (i.e. model_1). Thus, model_1 is stopped and model_2 starts running. For more information about the reconfiguration groups and dynamic reconfiguration in general, please refer to the Dynamic Reconfiguration section.

After the switch, we set another sleep time. This is because the target_switch_model_to_model function sends a STOP signal to the model being switched out as if target_stop_model was called. That is why we need the sleep time before getting the list of loaded models. If the sleep time was not set, the list of model could include the model being switched out as well. The target_is_model_loaded function returns 1 if the model given by its name is loaded and returns 0 otherwise. This function is useful in situations where you do not want to produce the list of all the loaded models, but rather to check if a model is loaded or not. The application waits for the user to press Enter before continuing on with the rest of the code, once the switch takes place. The target_kill_model function instantly kills the model's process. We do not recommend users to use this function. Use target_stop_model instead. Killing the process may result in loss of data. However, it is included in the code to demonstrate how to use it. After model_2 is also stopped, the list of loaded models is retrieved.

You can now build your application. As mentioned earlier, make sure the models you want to load are already downloaded on the target. You can then run your application and verify its functionality by constantly checking the list of processes for your target system or connecting to the real-time code from the model's Simulink window.

Warning Always make sure that you close the target handle at the end of your application. If you do not close the target handle, the next time you want to connect to the same target using the same target URI will result in errors, since the last connection is still open and the QUARC Target Manager is still trying to serve that connection which is no longer in use.

Uploading Files from a Target

The Target API contains 2 functions for uploading files from a target. These two functions are listed below:

We use a C++ application to demonstrate how to use these two functions to upload files from a target. This application first connects to a target and upon connecting, uploads a file using both of the above listed functions.

Before going into the details of the source code for our example, remember to include the header files containing the functions used in your code. For the Target API, you need to include the "quarc_target.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include "quanser_messages.h"
#include "quarc_target.h"

The first section of the source code is where the application attempts to connect to the target using the target URI specified. There is one additional function definition included in this section which is explained later. You can also refer to the comments for this function for more details. The following code segment shows this section.

#include "stdafx.h"

/*
	This function is used by the target_upload_file function to store the data from 
	uploaded file into the opened file specified by the file_handle.
*/
static t_int upload_file_callback(t_target dcp, t_long file_handle, t_int buffer_length, const t_byte * buffer)
{
	return _write(static_cast<int>(file_handle), buffer, buffer_length);
}

int _tmain(int argc, _TCHAR* argv[])
{
	
	/* The target handle used to establish a connection with the target. */
	t_target client_target;

	const char *	target_uri = "shmem://quarc-target:1";
	t_int receive_buffer_size = 1000;
	t_int send_buffer_size = 1000;

	/* This variable is used to store the returned value by the functions in the API. */
	t_error result;
	
	char message[512];
	
	/* 
		This function connects to a target using the specified target URI.
		The receive and send buffer sizes are also passed as parameters.
		The target handle declared above is used by this function. Once the
		target_connect function connects to a target, all the other functions 
		will use this handle to refer to the connected target. 
	*/	
	result = target_connect(target_uri, receive_buffer_size, send_buffer_size, &client_target);
		
	/* Print the appropriate message when the target_connect fails to connect to the target. */
	if ( result < 0)
	{
		msg_get_error_messageA(NULL, result, message, sizeof(message));
		printf("Unable to connect to the target. %s\n", message);
	}	
		
	/* Now connected. */
	if (result == 0)
	{

For a detailed description of the above code segment, you can refer to the Connecting to a Target section. The additional function defined in the source code is called upload_file_callback which is used by the target_upload_file to write the uploaded data from the target into an opened file. It accepts the target handle in addition to a file descriptor referring to the opened file, a buffer to store the uploaded data in and the buffer's length. This function calls another function called _write, which is included in the "io.h" header file, to write into the opened file.

The next section is where the application uploads a MAT-file from the target we are connected to using the target_upload_file function. It is shown by the following code segment.

		/* 
			The path of the file in which the uploaded data are stored. 
			Note that you need to create this file before attempting to
			open and write in it.
		*/
		static const char host_path[] = "C:\\Quanser\\example.mat";
		
		/* The relative path of the file to be uploaded from the target. */
		static const char target_path[] = "/Temp/example.mat";
		
		/* 
			This function opens the file specified by the path argument. We have used
			3 macros to define how the file should be opened. The file should be opened
			in binary (untranslated) format, caching is optimized for sequential access
			from hard disk and the file is opened for writing-only. The returned value 
			is a file descriptor for the opened file. In case of errors a value of -1 is
			returned.
		*/
		int file_handle = _open(host_path, _O_BINARY | _O_SEQUENTIAL | _O_WRONLY, 0);
		
		/* The _open function returned successfully. */
		if(file_handle != -1)
		{
			
			/* 
				An instant of the upload struct. This instant will be used 
				by target_upload_model function.
			*/
			t_upload upload_interface;
			
			/* 
				One of the members in the upload struct is a pointer to a function (called put_code).
				We set this pointer to point to the upload_file_callback function handle to write into
				the file specified by its descriptor.
			*/
			upload_interface.put_code = upload_file_callback;
			
			/* 
				This function uploads the file specified by target_path and uses the upload_interface
				struct to write the uploaded data into the file given by file_handle (or its file 
				descriptor).
			*/
			result = target_upload_file(client_target, target_path, &upload_interface, file_handle);
			
			/* Print the appropriate message in case of errors. */
			if( result < 0)
			{
				msg_get_error_messageA(NULL, result, message, sizeof(message)); 
				printf("Unable to upload the file from the target. %s\n", message);
			}
			else
				printf("The file %s has been successfully uploaded and stored in the file %s.\n", target_path, host_path);
			
			_close(file_handle);
		}

The path of the file in which the uploaded data are to be stored is given by the host_path string. Note that you have to create a file with ".mat" extension before being able to open it. Therefore, you need to make sure you have created the file with the proper format and set the host_path string to the path of the created file. The target_path string contains the path of the file on the target to be uploaded. Again, you have to make sure the file you want to upload already exists on the target machine and resides in the folder specified by the path. In our application, we are uploading a MAT-file from the target. MAT-files are generated through various methods. You can refer to the Streaming to Disk section of the QUARC documentation for details on how to save data in a MAT-file on a target.

The _open function opens the file defined by host_path and is defined in "io.h" header file. If it returned successfully, a file descriptor referring to the opened file is returned. Otherwise, a value of -1 is returned. We have used 3 macros to define how the file should be opened. The file should be opened in binary (untranslated) format, caching is optimized for sequential access from hard disk and the file is opened for writing-only. All these macros are defined in the "fcntl.h" header file. Once the file is opened successfully, we use the target_upload_file function to download the real-time code on the target we have connected to. But before invoking this function, an instant of the upload struct is initialized. This struct contains a pointer to a function called put_code which is set to the upload_file_callback function handle. Next in the code is the line where the target_upload_file function is invoked. This function uploads the specified file from the target. The pointer to the upload struct instant created earlier is passed as a parameter to this function, so the uploaded data can be written into the opened file. Once the upload operation is completed, the opened file is closed using the _close function defined in "io.h".

The next section is where the same file which was uploaded earlier gets uploaded again. This time, the target_upload_file_to_host function is used to upload the MAT-file. This section of the code is given by the following segment.

		/* 
			The path of the file in which the uploaded data are stored. 
			Note that you need to create this file before attempting to
			store data in it.
		*/
		static const char host_path_2[] = "C:\\Quanser\\Temp\\example.mat";
		
		/* 
			This function uploads a file from a target. The file is read from the 
			specified target_path and uploaded to the host, where it is stored in 
			the given host_path. Note that this is a high-level function with which
			you do not need to open a file and use an upload_file_callback function.
			This function is built upon the target_upload_file function.
		*/
		result = target_upload_file_to_host(client_target, target_path, host_path_2);
		
		/* In case of errors, print the appropriate message. */
		if( result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to upload the file from the target. %s\n", message);
		}
		else
			printf("The file %s has been successfully uploaded and stored in the file %s.\n", target_path, host_path_2);
	}
	/* Close the target handle. */
	target_close(client_target);
	getchar();
	return 0;
}

The host_path_2 string contains the path of the file in which the uploaded data are to be stored. Again, you should create a file with a ".mat" extension and set this string to the path of the created file. The target_upload_file_to_host function is the high-level function used to upload files from the target. You do not need to open and write into a file for uploading data form the target. All you need to do is to specify the path of the file on the target and the path of the file on the host. Note that this function is built upon the target_upload_file function. The target_upload_file function is mostly used in situations where the destination where the uploaded data are to be stored is something other than a file (e.g. an FTP location). In case you always save the uploaded data into a file, you do not need to use target_upload_file.

You can now build your application. Remember to create the host files and make sure that the MAT-file to be uploaded already exists on the target. You can then run your application and verify its functionality by opening the files containing the uploaded data. These two files should contain the same information as the MAT-file on the target.

Warning Always make sure that you close the target handle at the end of your application. If you do not close the target handle, the next time you want to connect to the same target using the same target URI will result in errors, since the last connection is still open and the QUARC Target Manager is still trying to serve that connection which is no longer in use.

Model Standard I/O

The Target API includes functions to access a model's standard input/output. You can use these function to get the model's standard output and write into the model's standard input. These functions are listed below:

We use a C++ application to demonstrate how to use the above listed functions to access a model's standard I/O. The application connects to a target using a target URI, opens a handle for the model's output, writes to the model's standard input (stdin) and gets the data being output by the model from its standard output (stdout).

Before going into the details of the source code for our example, remember to include the header files containing the functions used in your code. For the Target API, you need to include the "quarc_target.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include "quanser_messages.h"
#include "quarc_target.h"

The first section of the code is dedicated to four function definitions and a struct declaration which are described later. You can also refer to the comments associated with each function. The following code segment illustrates this section.

#include "stdafx.h"

/* 
	A struct used for the context of a thread. This struct will
	be used by model_terminated function to set the exit_code of
	the context of the thread that called the target_get_model_output 
	function.
*/
typedef struct tag_context
{
	t_int exit_code;
} t_context;

/*
	This function is used by the target_get_model_output function.
	This method is the first method called when the target_get_model_output 
    function is invoked and may be used for initialization prior to
    the other callback methods being invoked. This method should return 0.
    Returning a negative error code	will cause the target_get_model_output
    method to return with that error code.
*/
static t_int console_initialize(t_target dpc, t_model_output_handle handle, t_long context)
{
	return 0;
}

/*
	This function is used by the target_get_model_output function.
	This method is called when the model is downloaded. It is
	not called if the model is already downloaded at the time the
	target_get_model_output method is called, although it will be 
	called if the model is downloaded again.

	This method should return 0. Returning a negative error code
	will cause the target_get_model_output method to return
	with that error code.
*/
static t_int model_downloaded(t_target dpc, t_model_output_handle handle, t_long context, const char * model)
{
	return 0;
}

/*
	This function is used by the target_get_model_output function.
	This method is called when the model is about to load. It is
	not called if the model is already loaded at the time the
	target_get_model_output method is called,
	although it will be called if the model is terminated and then
	reloaded.

	This method should return 0. Returning a negative error code
	will cause the target_get_model_output method to return
	with that error code.
*/
static t_int model_loading(t_target dpc, t_model_output_handle handle, t_long context, const char * model)
{
	return 0;
}

/*
	This function is used by the target_get_model_output function.
	This method is called when a model has been loaded. It is
	not called if the model is already loaded at the time the
	target_get_model_output method is called,
	although it will be called if the model is terminated and then
	reloaded.

	This method should return 0. Returning a negative error code
	will cause the target_get_model_output method to return
	with that error code.
*/
static t_int model_loaded(t_target dpc, t_model_output_handle handle, t_long context, const char * model)
{
	return 0;
}

/*
	This function is used by the target_get_model_output function.
	This method is called when the model writes to stdout or stderr.
	It will display whatever that is being output by the model to the
	stdout(console).

	This method should return 0. Returning a negative error code
	will cause the target_get_model_output method to return
	with that error code.
*/
static t_int console_output(t_target dpc, t_model_output_handle handle, t_long context, const char * model, 
						t_int text_length, const t_byte * text)
{
	/* 
		This usage of printf function will print the "text_length" number 
		of characters in "text" passed as parameters to this function.
	*/
	printf("%.*s", text_length, text);
	return 0;
}

/*
	This function is used by the target_get_model_output
	function. This method is called when the model has terminated. 
	It is not called if the model is already unloaded at the time 
	the target_get_model_output method is called, although it will 
	be called if the model is loaded and then terminates again.

	This method should return 0. Returning a negative error code
	will cause the target_get_model_output method to return
	with that error code.
*/
static t_int model_terminated(t_target dpc, t_model_output_handle handle, t_long context, const char * model, t_int exit_code)
{
	/* 
		A pointer to the context struct. It is set to point 
		to the context variable passed to this function. This 
		way, we can modify the members of the struct being
		pointed to by "info". 
	*/
	t_context * info = (t_context *) context;
	
	/*
		The exit_code member of the struct being pointed at by
		"info" is set to "exit_code" variable passed to this function.
	*/
	info->exit_code = exit_code;

	/* 
		Stop getting console output and return from target_get_model_output.
		If we returned 0, then the target_get_model_output call would not return
		and we would continue to get notifications about the model loading/terminating/etc.
	*/
	return -QERR_WOULD_BLOCK;
}

The context struct is used for the context of a thread. This struct will be used by the model_terminated method to set the exit code of the context of the thread that called the target_get_model_output function. Its only member is an integer called exit_code which is the variable used to set the exit code. The four functions defined in the above section are all used by the target_get_model_output function. The way they are accessed by the target_get_model_output is by creating an instant of the model_output struct. This struct has four members which are pointers to function. These pointers to function are each set to the appropriate function handle from these 4 methods. The instant of the model_output struct is then passed to the target_get_model_output function as a parameter.

The model_downloaded method is called when the model is being downloaded. It is not called when the model is already downloaded at the time the target_get_model_output function is called, although it will be called if the model is downloaded again. The model_loading method is called when the model is about to load. It is not called if the model is already loaded at the time the target_get_model_output function is called, although it will be called if the model is terminated and then reloaded. The console_output function is called when the model writes to stdout or stderr. It displays the data being output by the model on the stdout of the application. The model_terminated method is called when the model has terminated. It is not called if the model is already unloaded at the time the target_get_model_output function is called, although it will be called if the model is loaded and then terminates again.

The next section is where the application connects to the target using a target URI and is illustrated by the following code segment.

int _tmain(int argc, _TCHAR* argv[])
{
	
	/* The target handle used to establish a connection with the target. */
	t_target client_target;

	const char *	target_uri = "shmem://quarc-target:1";
	t_int receive_buffer_size = 1000;
	t_int send_buffer_size = 1000;

	/* This variable is used to store the returned value by the functions in the API. */
	t_error result;
	
	char message[512];
	
	/* 
		This function connects to a target using the specified target URI.
		The receive and send buffer sizes are also passed as parameters.
		The target handle declared above is used by this function. Once the target_connect function
		connects to a target, all the other functions will use this handle to refer to the connected target. 
	*/	
	result = target_connect(target_uri, receive_buffer_size, send_buffer_size, &client_target);
	
	/* Print the appropriate message when the target_connect fails to connect to the target. */
	if ( result < 0)
	{
		msg_get_error_messageA(NULL, result, message, sizeof(message));
		printf("Unable to connect to the target. %s\n", message);
	}	
		
	/* Now connected. */
	if (result == 0)
	{

For information on the above code segment, please refer to the Connecting to a Target section in this documentation. The next section is where a handle to the model's output is created and the model's output is opened. It is shown by the code segment below.

		/* The name of the model to open its output. */
		const char * model_name = "test";
		
		/* 
			An instant of the context struct. This instant will 
			be used by target_get_model_output method. The thread 
			that invokes that function will have its context saved
			in this instant.
		*/
		t_context context;
		
		/* 
			An instant of model_output_handle. The target_open_model_output function will
			use this handle to get a specific model's output.
		*/
		t_model_output_handle handle;

		/* 
			This function opens a model's output given by its model name. It will
			use the instant of model_output_handle struct to get the model output.
			Once the model output is opened and assigned to the handle, all the other
			functions will use this handle to access the model output.
		*/
		result = target_open_model_output(client_target, model_name, &handle);
		
		/* Print the appropriate message in case of errors. */
		if (result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to open the model output. %s\n", message);
		}

		/* The model output is opened successfully. */
		if (result == 0)
		{

The model_name string is used to store the name of the model for which its output is being opened. The context variable is used by the target_get_model_output function. The thread that invokes that function will have its context saved in this variable. An instant of the model_output_handle struct is created which will be used by the target_open_model_output method to open a model's output. This function opens the model's output specified by its model name. Once the model output is opened and assigned to the instant of the model_output_handle struct, all other functions will use this handle to access the model's output. In successful return, a value of zero is returned, otherwise a negative error code is returned by this function.

The next section is where data get written to the model's input (stdin) and the model's output is retrieved. It is shown by the following code segment.

			/*
				This function writes data to the model's stdin. The model name
				along with the string to be input to the stdin of the model is
				passed as parameters to this function. You can use the Scan block
				in your model to retrieve the data being written to the model's stdin.
			*/
			result = target_write_model_input(client_target, "test", "1234\r");
			
			/* Print the appropriate message in case of errors. */
			if ( result < 0)
			{	
				msg_get_error_messageA(NULL, result, message, sizeof(message)); 
				printf("Unable to write to the model input. %s\n", message);
			}
			
			/* 
				An instant of the model_output struct. This instant 
				will be used by target_get_model_output function.
			*/
			t_model_output model_interface;

			/* 
				There are five pointer to function members in the model_output
				struct. Here, we assign each one of them to the appropriate
				function handle from the 5 functions defined at the beginning of
				the code.
			*/
            model_interface.initialize       = console_initialize;
			model_interface.model_downloaded = model_downloaded;
			model_interface.model_loading	 = model_loading;
			model_interface.console_output   = console_output;
			model_interface.model_terminated = model_terminated;

			/* 
				Immediately grab the console output and direct it to the console window
				(see console_output callback). The target_get_model_output function does
				not return until one of the callbacks returns a negative error code or
				target_close_model_output is called from another thread. All callbacks
				are invoked from the context of the thread that invokes
				target_get_model_output.
			*/
			result = target_get_model_output(client_target, handle, &model_interface, (t_long) &context);
			
			/* 
				-QERR_WOULD_BLOCK is returned by model_terminated callback 
				when executable finishes. If that is the case, the target_get_model_output
				function should have returned with no errors. That is why we set result
				to 0 again.
			*/
			if (result == -QERR_WOULD_BLOCK) 
				result = 0;
			
			/* Print the appropriate message in case of errors. */
			if( result < 0)
			{	
				msg_get_error_messageA(NULL, result, message, sizeof(message)); 
				printf("Unable to get the model output. %s\n", message);
			}
			
			/* 
				Once the model output is not needed anymore, close the
				model output handle.
			*/
			target_close_model_output(client_target, handle);
		}
	}
	/* Close the target handle. */
	target_close(client_target);
	
	printf("Press Enter to exit...\n");
	getchar();
	return 0;
}

The target_write_model_input function writes data to the model's input (stdin). The name of the model used to input data into its input along with the actual data is passed as parameters to this function. Note that the Simulink blocks do not support strings. Therefore, a number is being input to the model's input. You can use the Scan block in your Simulink model to retrieve the data being written to the model's stdin. Please refer to the Scan block reference page for details on how to use this block. An instant of the model_output struct is created and all of its members are assigned to the defined functions' handles. Once this assignment is performed, the console output is immediately grabbed and directed to the console window using the target_get_model_output function. This function, does not return until one of the callbacks (the 4 methods defined at the beginning of the source code) returns a negative error code or the target_close_model_output function is called from another thread. All callbacks are invoked from the context of the thread that invokes target_get_model_output.

When the model is terminated, the model_terminated callback function returns -QERR_WOULD_BLOCK as set in its definition. If that is the case, the target_get_model_output function should have returned with no errors. That is why we set result back to zero again. Once the model output is not needed anymore, it is closed using the target_close_model_output function.

You can now build your application. Since we are writing data to the model's input, we need to start the model before running the application. Otherwise, the target_write_model_input function will return with an error. However, if we did not have the lines for writing data to the model's input, we could have run the application before starting the model. The reason being the fact that the target_open_model_output function does not require the model to be running at the time it is called. It is a blocking function and blocks until the model starts and the actual output handle is created. To verify the functionality of the application, open a console for the model using the QUARC menu and compare the text being displayed on the model's console and the application's console. They should be the same since both refer to the model's output. Please refer to the Console item's description in the QUARC menu.

Warning Always make sure that you close the model output handle once you are done using it. If you do not close the model output handle, the next time you want to access it, you will encounter errors since the last time it was opened, it did not get closed.

Warning Always make sure that you close the target handle at the end of your application. If you do not close the target handle, the next time you want to connect to the same target using the same target URI will result in errors, since the last connection is still open and the QUARC Target Manager is still trying to serve that connection which is no longer in use.

Autoloading Models

The Target API contains functions to set models to load at boot. In other words, you can use these functions to set which models should be loaded once a target is booted. This feature is useful for embedded targets where you download the model into the target, set the model to be loaded at boot and turn the target machine off. Now, once the target is turned on again, the model will start running automatically. The functions to autoload models are listed below:

We use a C++ application to demonstrate how to use the above listed function to set your models to load at target boot. This application connects to a target specified by a target URI and then sets two models to load at boot. Next, the list of models to be loaded at target boot is retrieved. One of the model set to load at boot is then removed from the list and the updated list is retrieved.

Before going into the details of the source code for our example, remember to include the header files containing the functions used in your code. For the Target API, you need to include the "quarc_target.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include "quanser_messages.h"
#include "quarc_target.h"

The first section of the source code is where the application attempts to connect to the target using the target URI specified. There is one additional function definition included in this section which is explained later. You can also refer to the comments for this function for more details. The following code segment shows this section.

#include "stdafx.h"

/*
	This function is used by the model enumerating functions for printing out the model names.
	For example, target_get_loaded_models_at_boot uses this function to print out all the downloaded
	models.
*/
static t_int model_callback(t_target dpc, t_long context, const char * model_name, const char * model_arguments)
{
	printf("Model Name: %s - Model Arguments: %s\n", model_name, model_arguments);
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	/* The target handle used to establish a connection with the target. */
	t_target client_target;

	const char *	target_uri = "shmem://quarc-target:1";
	t_int receive_buffer_size = 1000;
	t_int send_buffer_size = 1000;

	/* This variable is used to store the returned value by the functions in the API. */
	t_error result;
	
	char message[512];
	
	/* 
		This function connects to a target using the specified target URI.
		The receive and send buffer sizes are also passed as parameters.
		The target handle declared above is used by this function. Once the target_connect function
		connects to a target, all the other functions will use this handle to refer to the connected target. 
	*/	
	result = target_connect(target_uri, receive_buffer_size, send_buffer_size, &client_target);
	
	/* Print the appropriate message when the target_connect fails to connect to the target. */
	if ( result < 0)
	{
		msg_get_error_messageA(NULL, result, message, sizeof(message));
		printf("Unable to connect to the target. %s\n", message);
	}	
		
	/* Now connected. */
	if (result == 0)
	{

For a detailed description of the above code segment, you can refer to the Connecting to a Target section. The function defined in the source code is called model_callback which is used by model enumerating functions to display the list of models. This function accepts the target handle in addition to model name and arguments and prints them out on the console.

The next section is where two models are set to load at boot. It is illustrated by the following code segment.

		/* 
			The name of the models that are to be loaded once the 
			computer boots. Note that these models should already
			be downloaded on the target.
		*/
		const char * model_1 = "example";
		const char * model_2 = "example2";

		/*
			This function sets a model to load at boot on the target.
			The model should already be downloaded on the target. The
			model to load at boot is specified by its name and arguments.
		*/
		result = target_load_model_model_at_boot(client_target, model_1, "");
		
		/* Print the appropriate message in case of errors. */
		if( result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to load the model at boot. %s\n", message); 
		}

		/*
			This function sets a model to load at boot on the target.
			The model should already be downloaded on the target. The
			model to load at boot is specified by its name and arguments.
		*/
		result = target_load_model_model_at_boot(client_target, model_2, "");
		
		/* Print the appropriate message in case of errors. */
		if( result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to load the model at boot. %s\n", message); 
		}

The names of the models that are to be loaded at target boot are stored in model_1 and model_2 strings. Note that the models to load at boot should already be downloaded on the target. Otherwise, attempting to set them to load at boot would result in error. Therefore, you have to download two models on the target and use their names to set mode_1 and model_2. You can also use either of the two functions in the Target API for downloading models which are explained in the Downloading Models section.

The target_load_model_model_at_boot function sets a model to load at boot on the target. The model's name and arguments are passed as parameters to this function along with the target handle, client_target. This function is called twice since we want to set two models to load at target boot.

The next section is where the list of models to be loaded at boot is retrieved, one of the models set to load at boot is removed from the list and the updated list is displayed. It is given by the following code segment.

		/* 
			This instant of model_enumeration struct is used by functions enumerating 
			models (e.g. target_get_models_loaded_at_boot). 
		*/
		t_model_enumeration model_interface;
		
		/*
			One of the members of the model_enumeration struct is a pointer to a function
			(called enumerate_model). We set this pointer to point to the model_callback
			function. This way, when the pointer to this struct is passed into model enumerating
			functions, the model name and arguments are printed out to the console.
		*/
		model_interface.enumerate_model = model_callback;
		
		printf("The following is the list of models set to load at boot:\n");
		
		/* 
			This function gets the list of all the models set to be loaded
			at boot. It uses the model_interface instant to enumerate the models.
		*/
		result = target_get_models_loaded_at_boot(client_target, &model_interface, NULL);
		
		printf("The model %s is removed from the list of models set to load at boot...\n", model_1);
		
		/*
			This function removes a model from being loaded at boot. It removes
			model_1 from the list of models being loaded at boot. In this application,
			it does not make sense to set a model to load at boot and then remove it
			from the list. Nevertheless, we have included this function to demonstrate 
			how to use it.
		*/
		result = target_remove_model_loaded_at_boot(client_target, model_1);
		
		if( result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to remove the model from the models to load at boot. %s\n", message); 
		}
		
		/* Get the updated list of models to be loaded at boot. */
		printf("The following is the updated list of models set to load at boot:\n");
		result = target_get_models_loaded_at_boot(client_target, &model_interface, NULL);
	}
	/* Close the target handle. */
	target_close(client_target);
	getchar();
	return 0;
}

An instant of model_enumeration struct is defined and initialized in the above code segment. This struct contains a pointer to a function called enumerate_model which is set to the model_callback function handle. This way, the target_get_loaded_models_at_boot) function can use this struct's instant to display the list of enumerated models. Once the list is displayed, model_1 is removed from the list of models to load at boot using the target_remove_model_loaded_at_boot function and the updated list is displayed.

You can now build your application. Remember to download the models and set the model names properly in the application before running it. You can then run your application and check the list of models to load at boot. Since we are using the host machine as the target as well, all you need to do to verify the functionality of the application is to restart your computer. Once restarted, you can check the list of processes to see if the model set to load at boot is indeed running.

Warning Once a model is set to load at boot on the target, it will always run once the target is booted. Therefore, if you want the model to load at boot only once, you need to remove the model from the list of models to load at boot after rebooting the target. You can do this using the target_remove_model_loaded_at_boot function or using the context menu popping up after right-clicking on a model's executable (real-time code). For information on how to use the context menu, please refer to the Do not run at boot menu item's description.

Warning Always make sure that you close the target handle at the end of your application. If you do not close the target handle, the next time you want to connect to the same target using the same target URI will result in errors, since the last connection is still open and the QUARC Target Manager is still trying to serve that connection which is no longer in use.

Configuring the QUARC Target Manager

The Target API contains functions to configure the QUARC Target Manager. The QUARC Target Manager is a small service or daemon running on each target system that allows QUARC to manage real-time code on the target. For more information about this service, please refer to the QUARC Target Manager section of the QUARC documentation. Using the mentioned functions, you can obtain information on the QUARC Target Manager's settings and configure them as well. These functions are listed below:

We use a C++ application to demonstrate how to use the above listed functions to configure the QUARC Target Manager. This application connects to a target using a target URI (note that by connecting to a target, we actually mean connecting to the QUARC Target Manager running on that target.), obtains the type of target connected to, adds two target URIs to the list of target URIs used by the QUARC Target Manager, retrieves the list of all the target URIs in service and finally remove one of the added target URIs.

Before going into the details of the source code for our example, remember to include the header files containing the functions used in your code. For the Target API, you need to include the "quarc_target.h" header file. In addition, another interface is used in our example to facilitate the interpretation of error codes. The header file for this API is called . For more information about this API, please refer to the Error Handling section.

The required header files are grouped into a common header file called stdafx.h in this example. The contents of this header file are:

#include <stdio.h>
#include "quanser_messages.h"
#include "quarc_target.h"

The first section of the source code is where the application attempts to connect to the target using the target URI specified. There is one additional function definition included in this section which is explained later. You can also refer to the comments for this function for more details. The following code segment shows this section.

#include "stdafx.h"

/*
	This function is used by the URI enumerating functions for printing out the URIs.
	For example, target_get_uris uses this function to print out all the target URIS.
*/
static t_int uri_callback(t_target dpc, t_long context, const char * key, const char * uri)
{
	printf("Protocol: %s - Target URI: %s\n", key, uri);
	return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
	
	/* The target handle used to establish a connection with the target. */
	t_target client_target;

	const char *	target_uri = "shmem://quarc-target:1";
	t_int receive_buffer_size = 1000;
	t_int send_buffer_size = 1000;

	/* This variable is used to store the returned value by the functions in the API. */
	t_error result;
	
	char message[512];
	
	/* 
		This function connects to a target using the specified target URI.
		The receive and send buffer sizes are also passed as parameters.
		The target handle declared above is used by this function. Once the target_connect function
		connects to a target, all the other functions will use this handle to refer to the connected target. 
	*/	
	result = target_connect(target_uri, receive_buffer_size, send_buffer_size, &client_target);
	
	/* Print the appropriate message when the target_connect fails to connect to the target. */
	if ( result < 0)
	{
		msg_get_error_messageA(NULL, result, message, sizeof(message));
		printf("Unable to connect to the target. %s\n", message);
	}	
		
	/* Now connected. */
	if (result == 0)
	{

For a detailed description of the above code segment, you can refer to the Connecting to a Target section. The function defined in the source code is called uri_callback which is used by the target_get_uris function to display the list of target URIs. This function accepts the target handle in addition to a variable called key which is the name of the protocol in use and uri which is the actual target URI. This function prints the last two variables on the console.

The next section is where the type of target is obtained in addition to the list of all the target URIs currently in available. It is shown by the following code segment.

		/* The URIs to be added to the list of target URI. */
		const char * uri_1 = "shmem://my-uri:1";
		const char * uri_2 = "shmem://my-uri:2";
	
		/* This key specifies the protocol of the URI to be added. */
		const char * key = "SHMEM";
	
		/* The target type in use is stored in this variable. */
		static char target_type[MAX_OPERATING_SYSTEM + 1];
		
		/* 
			An instant of the uri_enumeration struct. This instant will be used 
			by target_get_uris function to enumerate the target URIs.
		*/
		t_uri_enumeration uri_interface;
	
		/*
			One of the members of the uri_enumeration struct is a pointer to a function
			(called enumerate_uri). We set this pointer to point to the uri_callback
			function. this way, when the pointer to this struct is passed into URI enumerating
			functions, the URI name and protocol are printed out to the console.
		*/
		uri_interface.enumerate_uri = uri_callback;
		
		/*
			This function obtains the type of target currently connected.
			It stores the target type in target_type.
		*/
		result = target_get_target_type(client_target, target_type);
		
		/* In case of errors, print the error message. */
		if(result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to obtain the target type. %s\n", message);
		}
		
		/* 
			In case of successful operation by target_get_target_type, print out
			the target type.
		*/
		else
			printf("The type of target we are connected to is %s.\n", target_type);

		printf("The following is the list of target URIs defined:\n");
		
		/* 
			This function enumerates the target URIs associated with the target
			currently connected. It uses the uri_enumeration struct to enumerate
			these URIs.
		*/
		result = target_get_uris(client_target, &uri_interface, NULL);

The target URIs to be added to the list of serving URIs are stored in uri_1 and uri_2 strings. The key string contains the name of the protocol used by the URIs to be added. Here, we are only adding URIs using the shared memory protocol. If we were to add one URI using shared memory and one URI using TCP/IP, we would need to key variables. The reason being the fact that the key should match the protocol being used by the URI to be added. The target_type string will be used to store the type of target. An instant of uri_enumeration struct is defined and initialized in the above code segment. This struct contains a pointer to a function called enumerate_uri which is set to the uri_callback function handle. This way, the target_get_uris) function can use this struct's instant to display the list of enumerated URIs.

The target_get_target_type function retrieves the type of target we are connected to and stores the name of the target in target_type string. If the function returns successfully, the application prints out the name of the target on the console. The target_get_uris function lists all the currently defined target URIs using the instant created for the uri_enumeration struct.

The next section is where the two defined URIs are added to the target URIs, the updated list of target URIs is retrieved and one of the added URIs is removed from the list. The following code segment illustrates this section.

		/* 
			This function adds a URI to the list of target URIs. Once the
			URI is added, the QUARC Target Manager will also serve connections
			made to the added URI. The key variable specifies the protocol of
			the added URI. If the key variable points to a protocol not supported
			by QUARC Target Manager, an error code will be returned by this function.
		*/
		result = target_add_uri(client_target, key, uri_1);
		
		/* In case of errors, print the error message. */
		if(result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to add the URI %s. %s\n", uri_1, message);
		}
		
		/* 
			We add another URI to showcase how the target_remove_uri functions.
		*/
		result = target_add_uri(client_target, key, uri_2);
		
		/* In case of errors, print the error message. */
		if(result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to add the URI %s. %s\n", uri_2, message);
		}
		
		/* Print out the list of target URIs. */
		printf("The following is the list of target URIs after adding the new URIs:\n");
		result = target_get_uris(client_target, &uri_interface, NULL);

		/* 
			This function removes the URI associated with the protocol
			defined by the key variable. Now the key variable points to
			"SHMEM". We have added two URIs for this protocol. The way 
			target_remove_uri works is that it removes the latest URI added
			for the protocol specified. Hence, to remove the first URI (uri_1)
			you need to call this function twice. If you also want to remove
			the "shmem://quarc-target:1" URI, a third call to this function 
			is necessary.
		*/
		result = target_remove_uri(client_target, key);
		
		/* In case of errors, print the error message. */
		if(result < 0)
		{
			msg_get_error_messageA(NULL, result, message, sizeof(message)); 
			printf("Unable to remove the URI. %s\n", message);
		}
		
		/* Print the updated list of target URIs. */
		printf("This is the list of target URIs after removing a URI:\n");
		result = target_get_uris(client_target, &uri_interface, NULL);
	}
	/* Close the target handle. */
	target_close(client_target);

	getchar();
	return 0;
}

The target_add_uri function adds the URI specified by key and uri strings to the target URIs. Once the URI is added, the QUARC Target Manager we have connected to will also serve connections made to the added URI. The important note to mention is that if the key variable points to a protocol not supported by the QUARC Target Manager, an error code will be returned by this function. In our example, "shmem://my-uri:1" is the URI being added. Therefore, the next time you want to connect to the QUARC Target Manager running on the host machine (since we are using the shared memory protocol), you can use this target URI to connect to the target. The target_add_uri is called a second time to add the other URI as well. Please note that the URIs added are saved in a LIFO (last in, first out) queue. The reason for having this kind of storage structure becomes apparent when we explain the target_remove_uri function.

After adding the two URIs, the updated list of target URIs is retrieved. Next, the target_remove_uri function is called. If you look at the line where this function is invoked, you will notice that there is no URI name being passed as a parameter to this function. This function only accepts the key string corresponding to the name of a protocol in use. The way this function works is that it removes the latest URI added to the protocol specified by key. That is why the added URIs are saved in a LIFO queue. If we had added another URI for the shared memory protocol, that URI would have been the one being removed. Extra caution should be taken when removing URIs from the list of target URIs. If you only have one target URI for a protocol, calling the target_remove_uri function while passing that protocol as the parameter would result in the only target URI being removed. Therefore, the next time you want to connect to the target using that protocol would result in errors, since there is no URI set for that protocol. Another issue is that if you are currently connected to a target using a target URI which is the last one added to the target URIs for a protocol and you want to remove a URI from that protocol, you will encounter errors since the QUARC Target Manager cannot remove a URI in use.

Once the last added URI for shared memory protocol is removed, the updated list of target URIs is displayed, the target connection is closed and the application reaches its end.

You can now build your application and run it. In order to verify the functionality of your application, the next time you want to connect to the target, use "shmem://my-uri:1" instead of "shmem://quarc-target:1" as the target URI. You will notice that the connection gets established which verifies the proper operation of the application.

Warning As mentioned earlier, you have to take extra caution when using the target_remove_uri function to remove target URIs. Consider the following case; you have one URI for shared memory protocol and you call this function to remove a URI for the shared memory protocol. Since there is only one URI left in the list, it is going to be removed and you can never connect to the target using the shared memory protocol. The only solution would be to connect to the target using TCP/IP protocol and add a URI for the shared memory protocol.

Warning Always make sure that you close the target handle at the end of your application. If you do not close the target handle, the next time you want to connect to the same target using the same target URI will result in errors, since the last connection is still open and the QUARC Target Manager is still trying to serve that connection which is no longer in use.

 

navigation bar