Table of Contents > QUARC > User's Guide > QUARC External Interfaces > C Functions > Communications
Persistent Stream API
Persistent Stream API
The Quanser Persistent Stream API is used to create communication channels between C/C++ based applications and the real-time code generated for a Simulink model. The Persistent Stream API includes high-level C functions to create server/client applications which support persistent connectivity. This external interface is implemented using the Quanser Stream API and is meant to be used for basic communications. For more information about the QUARC Stream API, please refer to Stream API section.
In fact, the Stream Server and Stream Client blocks in the QUARC Targets/Communications/Basic library use the Persistent Stream API to operate within the MATLAB environment.
For details of these two blocks, please refer to Stream Server and Stream Client blocks reference pages.
Persistent connectivity means that the server/client will attempt to re-establish the connection when it is closed or lost. The Persistent Stream API is particularly valuable in applications like the deployment of cooperating UAVs (unmanned aerial vehicles), where you wish to communicate whenever another vehicle is within range. The Persistent Stream API takes the complexity out of maintaining the connection; you just send or receive data whenever there is a connection established. In addition, the Persistent Stream API is a high-level interface which does not require an extensive knowledge of communications.
Quanser software (e.g., QUARC, RCP) supports a variety of common communications protocols which are all accessed via the Quanser Persistent Stream API functions. The Persistent Stream API contains functions to connect to a remote host and to listen for client connections. For a complete list of the functions included in the Persistent Stream API, you can refer to the Persistent Stream API header file, .
The Persistent Stream API is designed to support any kind of communications channel. This flexibility is provided by loading protocol-specific drivers to handle a particular communications protocol. Hence, new communications protocols may readily be added without any changes to the functions of the Persistent Stream API. However, this flexibility requires that the Persistent Stream API have a means of specifying the communications protocol and any protocol-specific options. The protocol and options are provided through the use of Universal Resource Identifiers or URIs. For more information about URIs and the communications protocols supported by QUARC, please refer to Quanser Communications Protocols section of the QUARC documentation.
The URI is used when the communication channel is first established. To change the protocol in use, only the URI needs to change. For instance, consider an application using shared memory. In order to change the protocol to TCP/IP, all you need to do is to change the URI from shared memory to TCP/IP.
The user needs to be careful when using the UDP protocol. Be sure to read the documentation on the UDP protocol thoroughly, particularly on the different peer options. In particular, note that UDP is a datagram protocol and not a stream protocol, unlike TCP/IP. Hence, it is a connectionless protocol. Thus, it does not "listen" for client connections like the TCP/IP protocol. Therefore, listening for client connections does not establish a "listening" socket but simply results in a placeholder stream.
Likewise, "accepting" a client connection using UDP creates a socket that is bound to the port specified by the URI used for listening and returns immediately. In general, the peer to whom datagrams should be sent is established by the first client from whom a UDP datagram is received. Thus, for server connections, a receive operation should always be done before the first send operation.
Similarly, connecting to the server on the client side using UDP does not actually "connect" to a remote host. Instead, the client sets the address that will be used when sending packets. The first time a packet is sent the socket becomes implicitly bound to that address. Hence, in general, when using UDP protocol for communications on the client side, a send operation should always be before the first receive operation.
The Persistent Stream API operates in non-blocking mode, meaning that its functions will always return immediately. However, just like the Basic Stream blocks, it has two implementations that it supports internally. It can use non-blocking I/O, in which case it is single-threaded and uses non-blocking I/O for its communications, or it can create separate threads which do blocking I/O but communicate with the send/receive functions of the API via a FIFO queue so that the send/receive functions are still non-blocking, even though the actual I/O is done in a blocking thread. The reason for these two implementations is that some communications protocols do not support non-blocking I/O. Also, the multithreaded implementation tends to reconnect faster if the connection to the peer is lost and allows the pstream_send and pstream_receive functions to execute faster, since they need only check for items in their associated FIFO queues. Since the send/receive functions are always non-blocking, the C code for using both implementations is the same except for one of the options of the persistent stream used for communications.
The mode of operation only dictates the way functions or blocks work in a particular application or model. Therefore, it is possible to have a server operating in blocking mode and a client operating in non-blocking mode and vice versa.
This section serves as a guide on how to use the Quanser Persistent Stream API to establish connections between C/C++ applications and real-time code. The C/C++ based application can be acting as either a client trying to connect to the real-time code or as a server accepting connections from the real-time code. We will demonstrate both cases in this section. Since there is a not a significant change in the coding when changing the mode of operation, we will only demonstrate one mode of operation in each case. Before going into details, a few steps are required to setup your C/C++ application to use the Persistent Stream API. Please use the following list to refer to each topic:
Setting Up the C/C++ Application to Use the Persistent Stream API
The user needs to follow a sequence of steps to setup the C/C++ application to use the Persistent Stream API. We assume that Microsoft Visual Studio 2005 or later is being used to develop the application. Please perform the following procedure to setup your application, once in the Microsoft Visual Studio environment:
Open the Property Pages window by right-clicking on the project containing your application and choosing the menu item in the context menu. The following figure illustrates this window.
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.
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.
While in the Linker pane, click on the Input section. The first parameter in this section is called
Additional Dependencies. Type in 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.
Once all these changes are made, click on Apply and then OK.
C/C++ Application as Client
The C/C++ application may act as a client connecting to the real-time code. In this case, the real-time code functions as the server. In this section, one Simulink model is used to demonstrate the server side and one C++ application is used to demonstrate the client side.
Simulink Server Model
The example model used in this section is illustrated by the following figure.
The Stream Server block acts as the server in the communication process. It listens for and accepts a connection from a local or remote host and sends and/or receives data from that host. This block is a high-level block and does not require a sophisticated knowledge of communications to be used. For more information about this block, please refer to the Stream Server block help page. You can also refer to the Basic Communications section of the QUARC documentation in MATLAB to gain more insight on how to use this block. Even though the intermediate or advanced blocks are more flexible, we will not demonstrate how to use them in this example for the sake of simplicity. Information on how to use the intermediate or advanced communications blocks is provided in the Communications section of the MATLAB documentation for QUARC.
The data to be sent by the server model is a sine wave generated by a Sine Wave block. The data received by the server model is what is sent by the client application. In our case, the client/server system acts as a loop-back system. The data sent to the client gets sent back to the server by that client. In order to check the latency of our system, the data to be sent is plotted on a scope along with the actual received data from the client. The data sent is passed through a Memory block in order to compensate for the fact that the block operates at a fixed sample rate so the data received will not be available until the sampling instant immediately after it is received. In the ideal case, the two curves will be on top of each other, indicating that the latency is less than one sampling instant. Due to limited communications bandwidth, thread context switching latency and other factors, there will always be some latency in communications, although it will not be noticeable if the latency is less than one sampling instant.
There are a few parameters of the Stream Server block to be set. The following figure shows the Stream Server block properties window with parameters set accordingly.
Notice the field highlighted in blue. This field determines the mode of operation. You may choose either mode. By choosing the blocking mode, four other fields get enabled,
namely Send FIFO size in samples, Receive FIFO size in samples,
Send thread priority and Receive thread priority.
Use the default values for these parameters. For more information on these parameters, you can refer to the
Stream Server block help page in MATLAB. If you choose non-blocking mode, those four parameters
will be disabled. Another important parameter to consider is the
URI upon which to listen. It is set to shmem://foobar:1
which indicates that the server listens
on the shared memory buffer called "foobar" at port number 1.
Client Application
Before going into the details of the source code, remember to include the header files containing the functions used in your code. For the Persistent Stream API, you need to include the "quanser_persistent_stream.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 <signal.h> #include "quanser_messages.h" #include "quanser_thread.h" #include "quanser_persistent_stream.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" static int stop = 0; static void control_c_handler(int signum) { stop = 1; } int _tmain(int argc, _TCHAR* argv[]) { const char uri[] = "shmem://foobar:1"; const char * locale = NULL; /* The persistent stream for the client */ t_pstream client; /* The options structure for the persistent stream */ t_pstream_options options; /* The double variable used for sending/receiving of data */ t_double buffer; char message[512]; t_error result; /* These two instances of the qthread_attr_t structure refer to the send/receive thread attributes */ qthread_attr_t receive_thread_attributes; qthread_attr_t send_thread_attributes; /* Register a Ctrl+C handler to stop the code upon Ctrl+C being pressed */ signal(control_c_handler); /* The above instances are initialized here. */ qthread_attr_init(&receive_thread_attributes); qthread_attr_init(&send_thread_attributes); /* The persistent stream's send/receive thread attributes are set to the above created instances. We could set these fields to NULL to use the default thread attributes and avoid using qthread_attr_t variables but we show the use of the qthread_attr_t to demonstrate how thread attributes may be used. The functions for setting thread attributes are in */ options.receive_thread_attributes = &receive_thread_attributes; options.send_thread_attributes = &send_thread_attributes; /* Number of units to be sent/received with each pstream_send or pstream_receive call */ options.num_receive_units = 1; options.num_send_units = 1; /* Size of each unit to be sent. */ options.receive_unit_size = sizeof(t_double); options.send_unit_size = sizeof(t_double); /* Send/receive buffer sizes */ options.receive_buffer_size = 1000; options.send_buffer_size = 1000; /* The FIFO queue buffer sizes. These FIFO queues will be used if PSTREAM_FLAG_MULTITHREADED is set. */ options.send_fifo_size = 1000; options.receive_fifo_size = 1000; /* The persistent stream flags. */ options.flags = 0; /* Use blocking I/O. The pstream API is always non-blocking, but setting this flag will create a separate thread in which blocking I/O is done. But that thread communicates with the pstream_send and pstream_receive via the send/receive FIFO queues. The pstream_send and pstream_receive are always non-blocking and setting this flag will not change their mode of operation. */ options.flags |= PSTREAM_FLAG_MULTITHREADED; /* Set the other flags accordingly. Since we are looking at latency, use the most recent flags */ options.flags |= PSTREAM_FLAG_MINIMIZE_LATENCY | PSTREAM_FLAG_SEND_MOST_RECENT | PSTREAM_FLAG_RECEIVE_MOST_RECENT; /* The size of the options instance */ options.size = sizeof(options);
The uri
variable
is used by the pstream_connect
C function to connect to the server using the specified URI.
We have set this value to the URI configured in the Stream Server block. The options
variable is an instance of the t_pstream_options
structure, which contains the options for the client
persistent stream. The receive_thread_attributes
and send_thread_attributes
instances are initialized and then used as send/receive thread attributes of the options
structure. The other
variables of this structure are all set accordingly. Please refer to each line's comment for more information.
One of the most important variables of the options
structure is an unsigned integer called flags
.
If you look at the code, an OR operation is performed on this variable with the PSTREAM_FLAG_MULTITHREADED
constant to indicate that
blocking I/O is used as the mode of operation. In case you want to change the mode to non-blocking, you should perform an OR operation on this variable with
the PSTREAM_FLAG_NON_BLOCKING
constant. There are other constants added to the
flags
field which set different options for the persistent client stream.
The next section is where the client tries to connect to the server and upon connecting, sends and receives data. It is illustrated by the code segment below.
/* This function attempts to connect to the server side using the specified URI. The pstream API is non-blocking. Therefore, this function will return immediately. */ result = pstream_connect(uri, &options, &client); if (result == 0) /* now connected */ { buffer = 0.0; /* The loop where sending/receiving of data takes place. */ while (!stop) { /* This function receives data from the server. It is always non-blocking. */ result = pstream_receive(client, &buffer); if (result > 0) { /* This function sends data to the server side and is also non-blocking */ result = pstream_send(client, &buffer); } else if (result == -QERR_CONNECTION_NOT_BOUND) /* if UDP and socket not bound yet */ { /* This case is not required for most protocols. However, it is required for UDP when the pstream_receive is done first, as it is in this example. The reason is that the pstream_connect does not explicitly bind the UDP socket to the port, as the server does, because otherwise the client and server could not be run on the same machine. Hence, the pstream_connect implicitly binds the UDP socket to the port when the first pstream_send operation is performed. Thus, a pstream_send should always be executed prior to the first pstream_receive for the UDP protocol. Otherwise, the -QERR_CONNECTION_NOT_BOUND error is returned. Therefore, because this example does the pstream_receive first, we check for the -QERR_CONNECTION_NOT_BOUND error and issue a pstream_send in this instance. For other protocols, this case is never invoked, but the code is included here so that the protocol can be changed to UDP and the example left unchanged. */ result = pstream_send(client, &buffer); /* Send a value to get connection bound */ } /* Since the persistent stream maintains the connection, even reconnecting if necessary, then we don't really need to process the return value. We can continue to use the t_pstream even when errors are returned. However, since the pstream_send and pstream_receive functions are non-blocking we should really do other work or go to sleep so that we do not hog 100% of the CPU time. */ } /* If Ctrl+C is pressed, close the client stream. */ pstream_close(client); /* Print any error that occurred while trying to send/receive */ if (result < 0) { msg_get_error_messageA(locale, result,message, sizeof(message)); printf("Error communicating on URI '%s'. %s\n", uri, message); } } /* In case of errors in attempting to connect to the server, print the appropriate message. */ else { msg_get_error_messageA(locale, result, message, sizeof(message)); printf("Unable to connect to URI '%s'. %s\n", uri, message); } printf("Please press any key to exit...\n"); getchar(); return 0; }
The pstream_connect
function tries to connect to the server using the given URI. Since Persistent Stream API is always non-blocking,
this function will return immediately. If there are any errors in the attempt, it will return a negative error code. Otherwise, a value of zero
is returned. In case the function would block if it was operating in blocking mode, the -QERR_WOULD_BLOCK
error code is returned. The communication process should start only if a value of zero is returned by this function.
The while loop is where sending/receiving takes place. The pstream_receive
function is used to receive data from the client stream's
receive buffer. It is non-blocking and returns a positive value with successful execution. The order of executing functions is important in non-blocking mode. That is
why we have used an if statement to send the received data back to the server model using the pstream_send
function.
In case of errors in any of the mentioned operations, the program exits the while loop. Since the persistent stream maintains the
connection, even reconnecting if necessary, then we do not really need to process the return value. We can continue to use the t_pstream even when errors are returned. The only error
case being checked is whether an error code of -QERR_CONNECTION_NOT_BOUND
is returned. This case is not required for most protocols. However, it is
required for UDP when the pstream_receive
is done first, as it is in this example. The reason is that the pstream_connect
does not explicitly bind the UDP socket to the port, as the
server does, because otherwise the client and server could not be run on the same machine. Hence, the pstream_connect
implicitly binds the UDP socket to the port when the first
pstream_send
operation is performed. Thus, a pstream_send
should always be executed prior to the first pstream_receive
for the UDP protocol.
Otherwise, the -QERR_CONNECTION_NOT_BOUND
error is returned. Therefore, because this example does the pstream_receive
first, we check for the -QERR_CONNECTION_NOT_BOUND
error and issue a
pstream_send
in this instance. For the
shmem protocol, this case is never invoked, but the code is included here so that the protocol can be changed to UDP and the example left unchanged.
You can now build your Simulink model and the client application. To start the communication process, always remember to run the server side before the client side. Running the client before the server would cause the program to produce an error and exit. Therefore, you should run the Simulink model first and then the client application. Once the communication process starts, you can stop the server model and restart it again to verify the persistent connectivity of your application. You can look at the "Data Received" Scope block in the server model to see the plots of data received from the client and the data sent by the server which should be the same.
You can have any combination of operation modes for the server/client system. For instance, you can change the client's mode of operation to non-blocking while keeping the server in blocking mode and obtain the same results.
C/C++ Application as Server
The C/C++ application could be acting as a server accepting connections from the real-time code. In this case, the real-time code serves as the client. In this section, one Simulink model is used to demonstrate the client side and one C++ application is used to demonstrate the server side.
Simulink Client Model
The example model used in this section is illustrated by the following figure.
The Stream Client block acts as the client in the communication process. This block establishes a "persistent" connection with a host. The host may be local or remote. If the connection to the host is lost, this block automatically attempts to reconnect to the host. The Stream Client block is a high-level block and does not require a sophisticated knowledge of communications to be used. For more information about this block, please refer to the Stream Client block help pagein the MATLAB help for QUARC. You can also refer to the Basic Communications section of the QUARC documentation to gain more insight on how to use this block. Even though the intermediate or advanced blocks are more flexible, we will not demonstrate how to use them in this example for the sake of simplicity. Information on how to use the intermediate or advanced communications blocks is provided in the Communications section.
The data to be sent by the client model is a sine wave generated by a Sine Wave block. The received data is what is sent by the server application. In our case, the client/server system acts as a loop-back system. The data sent to the server gets sent back to the client by that server. In order to check the latency of our system, the data to be sent is plotted on a scope along with the actual received data from the server. The data sent is passed through a Memory block in order to compensate for the fact that the block operates at a fixed sample rate so the data received will not be available until the sampling instant immediately after it is received. In the ideal case, the two curves will be on top of each other, indicating that the latency is less than one sampling instant. Due to limited communications bandwidth, thread context switching latency and other factors, there will always be some latency in communications, although it will not be noticeable if the latency is less than one sampling instant.
There are a few block properties for the Stream Client block to be set. The following figure shows the Stream Client block properties window with parameters set accordingly.
Note that the blue highlighted field is where you choose the mode of operation. We have set this parameter to Use non-blocking I/O
to indicate that
the non-blocking I/O implementation is to be used. If you wanted to change the mode to blocking, you would have to set this parameter to
Use blocking I/O in a separate thread
. Notice that the first parameter, URI of host to which to connect,
is set to udp://localhost:18000
. The URI is modified to change the communications protocol
from the one used in the previous section to UDP protocol. As mentioned at the beginning of this document, a send operation should be performed
first on the client side to bind the socket when using the UDP protocol. However, binding the socket is performed internally in the
Stream Client block. For information on other parameters for this block, please refer to the
Stream Client block reference page in the MATLAB help for QUARC.
Server Application
Before going into the details of the source code, remember to include the header files containing the functions used in your code. For the Persistent Stream API, you need to include the "quanser_persistent_stream.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 <signal.h> #include "quanser_messages.h" #include "quanser_thread.h" #include "quanser_persistent_stream.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" static int stop = 0; static void control_c_handler(int signum) { stop = 1; } int _tmain(int argc, _TCHAR* argv[]) { const char uri[] = "udp://localhost:18000"; const char * locale = NULL; /* The persistent stream for the server */ t_pstream server; /* The options structure for the persistent stream */ t_pstream_options options; /* The double variable used for sending/receiving of data */ t_double buffer; char message[512]; t_error result; /* These two instances of the qthread_attr_t structure refer to the send/receive thread attributes */ qthread_attr_t receive_thread_attributes; qthread_attr_t send_thread_attributes; /* Register a Ctrl+C handler to stop the code upon Ctrl+C being pressed */ signal(SIGINT, control_c_handler); /* The above instances are initialized here. */ qthread_attr_init(&receive_thread_attributes); qthread_attr_init(&send_thread_attributes); /* The persistent stream's send/receive thread attributes are set to the above created instances. We could set these fields to NULL to use the default thread attributes and avoid using qthread_attr_t variables but we show the use of the qthread_attr_t to demonstrate how thread attributes may be used. The functions for setting thread attributes are in */ options.receive_thread_attributes = &receive_thread_attributes; options.send_thread_attributes = &send_thread_attributes; /* Number of units to be sent/received with each pstream_send or pstream_receive call */ options.num_receive_units = 1; options.num_send_units = 1; /* Size of each unit to be sent. */ options.receive_unit_size = sizeof(t_double); options.send_unit_size = sizeof(t_double); /* Send/receive buffer sizes */ options.receive_buffer_size = 1000; options.send_buffer_size = 1000; /* The FIFO queue buffer sizes. These FIFO queues will be used if PSTREAM_FLAG_MULTITHREADED is set. */ options.send_fifo_size = 1000; options.receive_fifo_size = 1000; /* The persistent stream flags. */ options.flags = 0; /* Use non-blocking I/O. The pstream API is always non-blocking and setting this flag will indicate that non-blocking I/O be used with no threads being created for communications. */ options.flags |= PSTREAM_FLAG_NON_BLOCKING; /* Set the other flags accordingly. Since we are looking at latency, use the most recent flags */ options.flags |= PSTREAM_FLAG_MINIMIZE_LATENCY | PSTREAM_FLAG_SEND_MOST_RECENT | PSTREAM_FLAG_RECEIVE_MOST_RECENT; /* The size of the options instance */ options.size = sizeof(options);
The uri
variable
is used by the pstream_listen
C function to listen to client connections on the specified URI.
We have set this value to the URI configured in the Stream Client block. The options
variable is an instance of the t_pstream_options
structure, which contains the options for the server
persistent stream. The receive_thread_attributes
and send_thread_attributes
instances are initialized and then used as send/receive thread attributes of the options
structure. The other
variables of this structure are all set accordingly. Please refer to each line's comment for more information.
One of the most important variables of the options
structure is an unsigned integer called flags
.
If you look at the code, an OR operation is performed on this variable with the PSTREAM_FLAG_NON_BLOCKING
constant to indicate that
non-blocking I/O is used as the mode of operation. In case you want to change the mode to blocking, you should perform an OR operation on this variable with
the PSTREAM_FLAG_MULTITHREADED
constant. There are other constants added to the
flags
field which set different options for the persistent server stream.
The next section is where the server listens for client connections and upon establishing a connection, sends/receives data. It is illustrated by the code segment below.
/* This function listens for new client connections using the specified URI. The pstream API is non-blocking. Therefore, this function will return immediately. */ result = pstream_listen(uri, &options, &server); if (result == 0)/* Now connected. */ { /* The loop where sending/receiving of data takes place. */ while(!stop) { /* This function receives data from the client. It is always non-blocking. */ result = pstream_receive(server, &buffer); if (result > 0) { /* This function sends data to the client side and it is non-blocking. */ result = pstream_send(server, &buffer); } /* Since the persistent stream maintains the connection, even reconnecting if necessary, then we don't really need to process the return value. We can continue to use the t_pstream even when errors are returned. However, since the pstream_send and pstream_receive functions are non-blocking we should really do other work or go to sleep so that we do not hog 100% of the CPU time. */ } /* If Ctrl+C is pressed, close the server stream. */ pstream_close(server); /* Print any error that occurred while trying to send/receive */ if (result < 0) { msg_get_error_messageA(locale, result,message, sizeof(message)); printf("Error communicating on URI '%s'. %s\n", uri, message); } } /* In case of errors in attempting to listen for client connections, print the appropriate message. */ else { msg_get_error_messageA(locale, result, message, sizeof(message)); printf("Unable to listen on URI '%s'. %s\n", uri, message); } printf("Please press any key to exit...\n"); getchar(); return 0; }
The pstream_listen
function listens for and accepts new client connections using the given URI. Since Persistent Stream API is always non-blocking,
this function will return immediately. If there are any errors while listening, it will return a negative error code. Otherwise, a value of zero
is returned. In case the function would block if it was operating in blocking mode, the -QERR_WOULD_BLOCK
error code is returned. The communication process should start only if a value of zero is returned by this function.
The while loop is where sending/receiving takes place. The pstream_receive
function is used to receive data from the connection stream's
receive buffer. It is non-blocking and returns a positive value with successful execution. The order of executing functions is important in non-blocking mode. That is
why we have used an if statement to send the received data back to the client model using the pstream_send
function.
Since the persistent stream maintains the connection, even reconnecting if necessary, then we do not really need to process the return value. We can continue to use the t_pstream even
when errors are returned.
In case of errors occurring while sending/receiving data, an appropriate message is printed. The server will continue to listen for new connections. Same situation applies to the case
where the client side has been shut down. If any errors occur while trying to execute the pstream_listen
function,
the appropriate message describing the nature of the error is printed, the server stream is closed and the user is prompted to press any key to exit the application.
You can now build your Simulink model and the server application. To start the communication process, always remember to run the server side before the client side. Running the client before the server would cause the program to produce an error and exit. Therefore, you should run the server application first and then the Simulink model. Once the communication process starts, you can look at the "Data Received" Scope block in the server model to see the plots of data received from the server and the data sent by the client which should be the same.
As mentioned at the beginning of this document, the user needs to be careful when using the UDP protocol. A send operation should be performed prior to receiving on the client side to bind the socket. However, since the Stream Client takes care of this ordering internally, all you need to do to change the protocol in use is to change the URI format.
You can have any combination of operation modes for the server/client system. For instance, you can change the server's mode of operation to blocking while keeping the client in non-blocking mode and obtain the same results.
Copyright ©2024 Quanser Inc. This page was generated 2024-10-17. Submit feedback to Quanser about this page.
Link to this page.