Main Content

Generate C++ Messages to Communicate Between Simulink and an Operating System or Middleware

To generate C++ code that supports message communication between Simulink top models and external applications, use the Simulink Messages & Events Library Send and Receive blocks. Generating code from a top model to facilitate messages passed outside of the Simulink environment enables your modeled application to communicate in a distributed system that uses an external message protocol service, commonly referred to as an operating system or middleware (for example, DDS, ROS, SOMEIP, or POSIX messages).

Simulink top models pass messages in the following way:

  • Top models contain message blocks to communicate outside the Simulink environment. If a top model contains a Send block directly connected to a root Outport block, the block converts its signals into messages and passes them outside the Simulink environment. If a top model contains a Receive block directly connected to a root Inport block, the block converts the received messages into signals.

  • The external message protocol manages the message communication according to its own standard (policies that control capacity, order of delivery, and other quality of service (QoS) behavior).

To generate C++ messages to communicate between Simulink top models and an operating system or middleware, prepare your model, generate code, and integrate that code with your selected operating system or middleware.

Prepare Model

To set up your model so that it can pass messages with an operating system or middleware, configure the model as a top model that has at least one message block (a Send block connected to a root Outport block or a Receive block connected to a root Inport block). The top model can then connect to your selected operating system or middleware through the message blocks as shown:

Within a model, the message ports are connected as shown:

Generate Code

To generate C++ code from a model:

  1. In the Apps gallery, click Embedded Coder.

  2. In the Configuration Parameters dialog box, set these parameters:

    • In the Code Generation pane, set the Language to C++.

    • In the Interface pane, set Code interface packaging to C++ class.

    • In the Templates pane, select Generate an example main program.

  3. Generate code. On the C++ Code tab, click Build.

  4. View the generated code. On the C++ Code tab, click View Code.

Integrate Code

To integrate the generated C++ code from your model with your selected operating system or middleware use handwritten code to implement send and receive message classes and application code that uses those classes to pass messages. Specifically:

  1. If you use your own main, create concrete subclasses from the generated abstract classes shown in RecvData_<T>.h and SendData<T>.h. If you use the generated example main program, concrete subclasses are provided for you in the file.

  2. Implement the class functions SendData and RecvData to call into your selected operating system or middleware to send and receive messages.

  3. Create an instance of your implemented send and receive message classes (send and receive objects).

  4. Create an instance of the model class by using the instances of each message class (send and receive objects) as arguments in the model constructor.

  5. Send and receive messages as required by your application. You are expected to manage the lifetime of the message queues. It is required that the queues are in a state ready to accept messages prior to the first step of the model.

To integrate:

  1. Open the generated example main program (or create your own). If you use the generated example main program, the concrete subclasses RecvData_real_T and SendData_real_T are provided in the file. If you create your own main, create concrete subclasses in your application code:

  2. To receive messages, hand write the implementation of the generated receive class. Implement the class function RecvData to call into your selected operating system or middleware to receive messages.

    An example implementation for POSIX is shown:

    class mHMIHandlerRecvData_real_T: public RecvData_real_T {
        public:
        void RecvData(real_T* data, int32_T length, int32_T* status)
        {
            // Use POSIX API mq_receive to receive messages
            unsigned int priority = 1;
            *status = mq_receive(msgQueue, (char *)data, length, &priority);
        }
    };
    

    Create a receive object.

    static mHMIHandlerRecvData_real_T InMsgRecvData_arg;
    
  3. To send messages, hand write the implementation of the generated send class. Implement the class function SendData to call into your selected operating system or middleware to send messages.

    An example implementation for POSIX is shown:

    class mHMIHandlerSendData_real_T : public SendData_real_T {
        public:
        void SendData(const real_T* data, int32_T length, int32_T* status)
        {
            // Use the POSIX API mq_send to send messages
            unsigned int priority = 1;
            *status = mq_send(msgQueue, (char*)data, length, priority);
        }
    };
    

    Create a send object.

    static mHMIHandlerSendData_real_T OutMesgSendData_arg;
    

    Create an instance of the model class by using your send and receive objects as arguments in the model constructor.

    static mHMIHandler mHMI_Obj(InMsgRecvData_arg, OutMsgSendData_arg);
    
  4. Send and receive messages as required by your application and maintain the lifetime of the message queue.

    An example implementation for POSIX is shown:

    int_T main(int_T argc, const char *argv[])
    {
        // Unused arguments
        (void)(argc);
        (void)(argv);
    
        //Initialize model
        mHMI_obj.initialize();     
    
        // Open POSIX queue
         mqd_t msgQueue = mq_open("/PosixMQ_Example", O_RDONLY);
         if (msgQueue == -1)
         {
             printf("mq_open failed\n");
             exit(1);
         }     
    
        // Send and Receive messages
         while (rtmGetErrorStatus(mHMI_Obj.getRTM()) == (NULL)) {
             //perform application tasks here.
             rt_OneStep();
         }   
    
        // Close POSIX queue
         mq_close(msgQueue);     
    
        // Terminate model
         mHMI_Obj.terminat():
         return 0;
     }

For a more complex POSIX integration example, see Use Handwritten Code to Integrate C++ Messages with POSIX

Considerations and Limitations

  • Handwritten code is the only supported integration technique.

  • The parameter Generate an example main program must be selected. Applications that require a static main are not supported.

  • Function prototype control (FPC) cannot be configured for a top model that has root message ports.

Related Topics