Migrating Basilisk Modules from Version 1.X to 2.X

Motivation

This document discusses what coding related changes occurred with the new messaging system in Basilisk version 2.0 and higher. However, nothing is for free. Making these changes was not possible without breaking existing code. This migration help page outlines all the Basilisk coding related changes that have occurred. This facilities the process of upgrading legacy Basilisk C and C++ code to function with the new message system, etc.

For changes related to updating Basilisk python scripts, see Migrating Basilisk Scripts from Version 1.X to 2.X.

Message Names

See Migrating Basilisk Scripts from Version 1.X to 2.X for the discussion on how module input and output messages no longer have name and ID variables.

Creating New Message Definitions

See Message Definitions for how new message C structures can be created and added to the Basilisk project.

Step-By-Step Guide to Upgrade a Basilisk Modules

All the existing BSK 1.x messaging functionality exists with the new BSK 2.0 messaging system. This facilitates the migration to 2.0 as it requires some commands to be changed, but none of the code logic should have to change. As before, the message interfaces vary between C and C++ modules.

Changed Importing of Some Files

If your module uses:

#include "simulation/simFswInterfaceMessages/macroDefinitions.h"

this file has now moved to:

#include "architecture/utilities/macroDefinitions.h"

Further, all includes should be done relative to the Basilisk/src directory. This means:

#include "sensors/magnetometer/magnetometer.h"

should be changed to:

#include "simulation/sensors/magnetometer/magnetometer.h"

Updating a C Module

  1. Updating the module.h file:

    • Remove the import of the C interface to the old messaging system, delete this line

      #include "messaging/static_messaging.h"
      

      The message wrappers being included below replace this step.

    • Update the #include statement to read in the message definition through

      #include "cMsgCInterface/ModuleMsg_C.h"
      

      This import provides access to the message object as well as the C message structure definition.

    • Replace the char message name variable moduleOutMsgName and associated int_32t message ID variable moduleOutMsgId with

      ModuleMsg_C moduleOutMsg;    // sensor output message
      ModuleMsg_C moduleInMsg;     // sensor input message
      

      Note that the Fsw, Int and Sim message distinctions are now dropped in the new messaging system. This step is the same regardless if this message connects to/from a C or C++ Basilisk module.

    • If you want to create an array of output messages, this can be done with

      SomeMsg_C descriptionOutMsgs[10];
      
    • Remove the CrossInit_xxxx() method, it is no longer used. If you initialized any code in this function, move that code to the module Reset_xxxx() function.

    • Clean up the SelfInit_xxxx() method to only initialize the output messages. All other code and setup should be moved to the Reset_xxxx() function.

  2. Updating the module.c file:

    • To initialize the module output message in SelfInit_xxxx(), replace

      configData->moduleOutMsgId = CreateNewMessage(configData->moduleOutMsgName,
       sizeof(ModuleFswOutMsg), "ModuleFswOutMsg", moduleID);
      

      with

      ModuleMsg_C_init(&configData->moduleOutMsg);
      
    • To check if an output message has been linked to other input message, use

      ModuleMsg_C_isLinked(&configData->moduleOutMsg);
      
    • To connect to an input message, delete

      configData->moduleInMsgId = subscribeToMessage(configData->moduleInMsgName,
                                              sizeof(ModuleFswMsg), moduleID);
      

      The input messages are connected when then Basilisk simulation is scripted in python. No additional code is required in your C code. Remove the CrossInit_xxxx() method, it is no longer used. If you initialized any code in this function, move that code to the module Reset_xxxx() function.

    • To create a local variable of the message content structure (payload) itself, use

      ModuleMsgPayload msgBuffer;
      
    • To read in a message, replace

      ModuleFswMsg msgBuffer;
      memset(&msgBuffer, 0x0, sizeof(ModuleFswMsg));
      ReadMessage(configData->moduleInMsgId, &timeOfMsgWritten, &sizeOfMsgWritten,
                  sizeof(ModuleFswMsg), (void*) &(sc), msgBuffer);
      

      with

      ModuleMsgPayload msgBuffer;
      msgBuffer = ModuleMsg_C_read(&configData->moduleInMsg);
      
      • To check is a message has been connected to, check the value of ModuleMsg_C_isLinked()

      • To check if a message has ever been written to, check the value of ModuleMsg_C_isWritten()

      • To get the time when a message was written, use ModuleMsg_C_timeWritten()

      • To get the ID of the module who wrote the message, use ModuleMsg_C_moduleID()

    • To zero a message payload variable someMsgBuffer of type SomeMsgPayload, while enjoying strong type checking, you can remove the use of memset() and use instead

      SomeMsgPayload someMsgBuffer;
      someMsgBuffer = SomeMsg_C_zeroMsgPayload();
      
    • To write to an output message, assuming outputMsgBuffer is a local variable holding the message content (payload), replace

      memset(&outputMsgBuffer, 0x0, sizeof(ModuleFswMsg));
      outputMsgBuffer.variable = 42;     // specify output msg values
      WriteMessage(configData->moduleOutMsgId, callTime, sizeof(ModuleIntMsg),
              (void*) &(outputMsgBuffer), moduleID);
      

      with

      outputMsgBuffer = ModuleMsg_C_zeroMsgPayload();
      outputMsgBuffer.variable = 42;      // specify output msg values
      ModuleMsg_C_write(&outputMsgBuffer, &configData->moduleOutMsg, moduleID, callTime);
      

      Note that you should still zero the local outputMsgBuffer structure in C Modules such that the message has zero default values if some fields are note set.

  3. Updating the module.i file:

    • In the GEN_SIZEOF() commands used to be used to get the size of a message in Python. This is no longer required with the new message system. Thus, these GEN_SIZEOF() commands can be removed. To create and access messages from Python the message2 package is now used.

    • Update the #include statement and add the struct statement to read

      %include "architecture/msgPayloadDefC/ModuleMsgPayload.h"
      struct ModuleMsg_C;
      
    • Any custom Swig’d interfaces to access message content, such as

      ARRAYASLIST(FSWdeviceAvailability)
      

      should be removed from the module.i file and moved to src/architecture/messaging/messaging.i file instead. These interfaces can now be used by any module by importing messages2 in the Basilisk python script.

    • If you want to create an array of output messages SomeMsg_C, the array of messages must be swig’d to be accessible from python. In the module *.i file, add this statement

      STRUCTASLIST(SomeMsg_C)
      
    • The location of swig_common_model has moved to architecture. Thus, replace:

      from Basilisk.simulation.swig_common_model import *
      

      with:

      from Basilisk.architecture.swig_common_model import *
      
  4. Updating the module.rst documentation file:

    • In the table of module messages, update any message variable names that were changed as well as the message definition from SomeFswMsgPayload to SomeMsgPayload.

    • If applicable, update the module msg I/O illustration

Updating a C++ Module

  1. Updating the module.h file:

    • Update the #include statement to read in a C message definition through

      #include "architecture/msgPayloadDefC/SomeMsgPayload.h"
      

      To include a C++ message definition use

      #include "architecture/msgPayloadDefCpp/SomeMsgPayload.h"
      
    • Replace the include statement for the old message system

      #include "architecture/messaging/system_messaging.h"
      

      with the include for the new message system

      #include "architecture/messaging/messaging.h"
      
    • Remove both the SelfInit() and CrossInit() methods, they are longer used. If you initialized any code in these functions, move that code to the module Reset() method.

    • For output messages, replace the std::string message name variable moduleOutMsgName and associated int_32t message ID variable moduleOutMsgId with the public variable:

      Message<OutputMsgPayload>  moduleOutMsg;    //!< sensor output message
      

      This creates an instance of the output message object that is contained within this module.

    • For input messages, replace the std::string message name variable moduleInMsgName and associated int_32t message ID variable moduleInMsgId with the public functor:

      ReadFunctor<InputMsgPayload>   moduleInMsg;     //!< sensor input message
      
    • It is possible to create a vector of output message pointers of type SomeMsgPayload using

      std::vector<Message<SomeMsgPayload>*> descriptionOutMsgs;
      
    • Similarly, you can create a vector of input message reader objects of type SomeMsgPayload using the following statement. Note that you can directly create message reader instances, and not pointers to such objects as with a vector of output messages.

      std::vector<ReadFunctor<SomeMsgPayload>> descriptionInMsgs;
      
  2. Updating the module.cpp file:

    • There is no need for additional code to create an output connector. Thus, delete old message creation code such as:

      this->moduleOutMsgId = SystemMessaging::GetInstance()->CreateNewMessage(this->moduleOutMsgName,
                                                                          sizeof(ModuleSimMsg),
                                                                          this->numOutMsgBuffers,
                                                                          "ModuleSimMsg", this->moduleID);
      

      The new message object is automatically created through the above process in the module.h file. In fact, deleted the SelfInit() method as it is no longer needed with C++ modules. The output message object automatically connect to themselves in their constructors. Any other code in the SelfInit() method should be moved to the Reset() method.

    • If a std::vector of output message pointers of type SomeMsgPayload was created in the module *.h file then these message objects must be created dynamically in the *.cpp code using

      Message<SomeMsgPayload> *msg;
      msg = new Message<SomeMsgPayload>;
      this->descriptionOutMsgs.push_back(msg);
      

      Don’t forget to delete these message allocation in the module deconstructor.

    • If you have a std::vector of input message objects, these are typically provided to the BSK module in Python through a support function. For example, consider the case where the module has a vector of planet state input messages that can be configured. The method addPlanet() then receives the message object pointer as shown below. Next, the vector planetInMsgs must have a reader to the provided message object added. The message method .addSubscriber() returns a reader object, essentially an input message linked to this output message. The code below assumes the module also has a std::vector of the planet state payload structure to act as the input buffer variables. This addPlanet routine below creates such buffer variables and adds them to the planetMsgData vector each time a planet is added.

       void BskModuleName::addPlanet(Message<SpicePlanetStateMsgPayload> *planetSpiceMsg)
      {
          /* add a message reader to the vector of input messages */
          this->planetInMsgs.push_back(planetSpiceMsg->addSubscriber());
      
          /* expand the planet state input buffer vector */
          SpicePlanetStateMsgPayload plMsg;
          this->planetMsgData.push_back(plMsg);
      }
      
    • To check is an output message has been connected to, check the value of this->moduleOutMsg.isLinked()

    • To subscribe to an input message, this is now accomplished in the Basilisk Python script where the message to module connections are setup now. Thus, delete code such as this:

      this->moduleInMsgID = SystemMessaging::GetInstance()->subscribeToMessage(this->moduleInMsgName,
                                                                             sizeof(ModuleFswMsg), moduleID);
      

      Remove the CrossInit() method, it is longer used. If you initialized any code in this method, move that code to the module Reset() method.

    • To read an input message, replace old code such as:

      InputFswMsg moduleInMsgBuffer;
      memset(&moduleInMsgBuffer, 0x0, sizeof(InputFswMsg));
      this->moduleInMsg =
         SystemMessaging::GetInstance()->ReadMessage(this->moduleInMsgID, &LocalHeader,
                                                  sizeof(InputFswMsg),
                                                  reinterpret_cast<uint8_t*> (&(moduleInMsgBuffer)),
                                                  moduleID);
      

      with this new code:

      InputMsgPayload moduleInMsgBuffer;
      moduleInMsgBuffer = this->moduleInMsg();
      

      Take a moment to marvel at the simplicity of this message reading!

      • To check is an input message has been connected to, check the value of this->moduleInMsg.isLinked()

      • To check if a message has ever been written to, check the value of this->moduleInMsg.isWritten()

      • To get the time when a message was written, use this->moduleInMsg.timeWritten()

      • To get the ID of the module who wrote the message, use this->moduleInMsg.moduleID()

    • To zero a local message structure variable someMsgBuffer of type SomeMsgPayload, remove the use of memset() and rather use the following. If the msg buffer variable is for use with an output message someOutMsg, then use

      SomeMsgPayload someMsgBuffer;
      someMsgBuffer = this->someOutMsg.zeroMsgPayload;
      

      If the buffer is related to an input message someInMsg, the same basic syntax works. Just replace someOutMsg with someInMsg above. This ensures the correct message type is zero’d and assigned to the local buffer variable.

    • To write to an output message, replace this old code:

      SystemMessaging::GetInstance()->WriteMessage(this->moduleOutMsgId, clockTime, sizeof(OutputSimMsg),
                                              reinterpret_cast<uint8_t*> (&outMsgBuffer), this->moduleID);
      

      with this new code:

      this->moduleOutMsg.write(&outMsgBuffer, this->moduleID, clockTime);
      

      Again, stop and marvel.

  3. Updating the module.i file:

    • The location of swig_common_model has moved to architecture. Thus, replace:

      from Basilisk.simulation.swig_common_model import *
      

      with:

      from Basilisk.architecture.swig_common_model import *
      
    • In the GEN_SIZEOF() commands used to be used to get the size of a message in Python. This is no longer required with the new message system. Thus, these GEN_SIZEOF() commands can be removed. To create and access messages from Python the message2 package is now used.

    • Update the C message definition include statement from

      %include "simMessages/OutputSimMsg.h"
      

      to use the new common message folder location

      %include "architecture/msgPayloadDefC/OutputMsgPayload.h"
      struct OutputMsg_C;
      

      If including a C++ message payload definition, then only use:

      %include "architecture/msgPayloadDefCpp/OutputMsgPayload.h"
      
    • Any custom Swig’d interfaces to access message content, such as

      %template(RWConfigVector) vector<RWConfigSimMsg>;
      

      should be removed the module.i file and moved to src/architecture/messaging/messaging.i file instead. These interfaces can now be used by any module by importing messaging in the Basilisk python script.

    • To create the swig interface to a vector of output message pointers of type SomeMsgPayload, near the bottom of the messaging.i file add this line:

      %template(SomeOutMsgsVector) std::vector<Message<SomeMsgPayload>*>;
      
  4. Updating the module.rst documentation file:

    • In the table of module messages, update any message variable names that were changed as well as the message definition from SomeFswMsgPayload to SomeMsgPayload.

    • If applicable, update the module msg I/O illustration

    • If there are links to message types in the source method descriptions, update these to use the new message payload declaration.