# ISC License
#
# Copyright (c) 2021, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""
This script is used to create a Basilisk module folder given the basic I/O and naming information.
- Modify either ``fillCppInfo()`` or ``fillCInfo()`` to contain the desired information for the new BSK module.
- edit the ``__main__`` routine at the end of the file to call the desired module type with
``createCppModule()`` or ``createCModule``.
- run the script from the command line using ``python3 makeDraftModule.py``
"""
import os
import re
import shutil
from datetime import datetime
# assumes this script is in .../basilisk/src/utilities
pathToSrc = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
initialCwd = os.getcwd()
statusColor = '\033[92m'
failColor = '\033[91m'
warningColor = '\033[93m'
endColor = '\033[0m'
[docs]
class moduleGenerator:
"""
class to generate draft Basilisk modules
"""
def __init__(self):
# the following variables must be set for this module generator to function
self.modulePathRelSrc = None # path to the new module folder relative to basilisk/src
self.moduleName = None # lower camel case name of the module
self.briefDescription = None # brief module description
self.copyrightHolder = None # holder of open source copyright
self.inMsgList = [] # list of input message dictionary list
self.outMsgList = [] # list of input message dictionary list
self.variableList = [] # list of module variables
# module behavior flags
self.cleanBuild = False # flag if any prior directories should be deleted automatically
self.verbose = True # flag if the status messages should be printed
# private class variables
self._absPath = None # absolute path to the folder which will contain the module folder
self._newModuleLocation = None # absolute path to the auto-generated Basilisk module folder
self._licenseText = None # BSK open-source license statement
def log(self, statement, **kwargs):
if self.verbose:
if 'end' in kwargs:
endString = kwargs['end']
print(statement, end=endString)
else:
print(statement)
[docs]
def checkPathToNewFolderLocation(self):
"""
Make sure the supplied module destination path is correct
"""
self.log(f"{statusColor}Checking Module location:{endColor}", end=" ")
if os.path.isdir(self._absPath):
os.chdir(self._absPath)
else:
self.log(f"{failColor}\nERROR: {endColor}Incorrect path to the new folder:")
self.log(self._absPath)
exit()
self.log("Done")
self.log(self._absPath)
[docs]
def createNewModuleFolder(self):
"""
Create the new module folder
"""
self.log(f"{statusColor}Creating Module Folder:{endColor}", end=" ")
if os.path.isdir(self._newModuleLocation):
self.log(f"\n{warningColor}WARNING: {endColor}The new module destination already exists.")
if not self.cleanBuild:
ans = input("Do you want to delete this folder and recreate? (y or n): ")
if ans != "y":
self.log(f"{failColor}Aborting module creation.{endColor}")
exit()
self.log("Cleared the old folder.")
shutil.rmtree(self._newModuleLocation)
else:
self.log("Done")
os.mkdir(self._newModuleLocation)
os.chdir(self._newModuleLocation)
[docs]
def readLicense(self):
"""Read the Basilisk license file"""
self.log(statusColor + "Importing License:" + endColor, end=" ")
with open(pathToSrc + "/../LICENSE", 'r') as f:
self._licenseText = f.read()
self._licenseText = self._licenseText.replace("2016", str(datetime.now().year))
self._licenseText = self._licenseText.replace(
"Autonomous Vehicle Systems Lab, University of Colorado at Boulder",
self.copyrightHolder)
self.log("Done")
[docs]
def createRstFile(self):
"""Create the Module RST documentation draft."""
rstFileName = f"{self.moduleName}.rst"
self.log(f"{statusColor}Creating RST Documentation File {rstFileName}:{endColor}", end=" ")
rstFile = 'Executive Summary\n'
rstFile += '-----------------\n'
rstFile += f'{self.briefDescription}\n'
rstFile += '\n'
rstFile += 'Message Connection Descriptions\n'
rstFile += '-------------------------------\n'
rstFile += 'The following table lists all the module input and output messages. \n'
rstFile += 'The module msg connection is set by the user from python. \n'
rstFile += 'The msg type contains a link to the message structure definition, while the description \n'
rstFile += 'provides information on what this message is used for.\n'
rstFile += '\n'
rstFile += '.. list-table:: Module I/O Messages\n'
rstFile += ' :widths: 25 25 50\n'
rstFile += ' :header-rows: 1\n'
rstFile += '\n'
rstFile += ' * - Msg Variable Name\n'
rstFile += ' - Msg Type\n'
rstFile += ' - Description\n'
for msg in self.inMsgList + self.outMsgList:
rstFile += f' * - {msg["var"]}\n'
rstFile += f' - :ref:`{msg["type"]}Payload`\n'
rstFile += f' - {msg["desc"]}\n'
rstFile += '\n'
with open(rstFileName, 'w') as w:
w.write(rstFile)
self.log("Done")
[docs]
def createTestFile(self, type):
"""
Create a functioning python unit test file that loads the new module, creates and connect blank
input messages, and sets up recorder modules for each output message.
"""
os.mkdir('_UnitTest')
os.chdir('_UnitTest')
testFileName = f"test_{self.moduleName}.py"
self.log(f"{statusColor}Creating Python Init Test File {testFileName}:{endColor}", end=" ")
testFile = ""
for line in self._licenseText.split('\n'):
testFile += f'# {line}\n'
testFile += '\n'
testFile += 'import pytest\n'
testFile += '\n'
testFile += 'from Basilisk.utilities import SimulationBaseClass\n'
testFile += 'from Basilisk.utilities import unitTestSupport\n'
testFile += 'from Basilisk.architecture import messaging\n'
testFile += 'from Basilisk.utilities import macros\n'
testFile += f'from Basilisk.{os.path.split(self.modulePathRelSrc)[0]} import {self.moduleName}\n'
testFile += '\n'
testFile += '@pytest.mark.parametrize("accuracy", [1e-12])\n'
testFile += '@pytest.mark.parametrize("param1, param2", [\n'
testFile += ' (1, 1)\n'
testFile += ' ,(1, 3)\n'
testFile += '])\n'
testFile += '\n'
testFile += f'def test_{self.moduleName}(show_plots, param1, param2, accuracy):\n'
testFile += ' r"""\n'
testFile += ' **Validation Test Description**\n'
testFile += '\n'
testFile += ' Compose a general description of what is being tested in this unit test script.\n'
testFile += '\n'
testFile += ' **Test Parameters**\n'
testFile += '\n'
testFile += ' Discuss the test parameters used.\n'
testFile += '\n'
testFile += ' Args:\n'
testFile += ' param1 (int): Dummy test parameter for this parameterized unit test\n'
testFile += ' param2 (int): Dummy test parameter for this parameterized unit test\n'
testFile += ' accuracy (float): absolute accuracy value used in the validation tests\n'
testFile += '\n'
testFile += ' **Description of Variables Being Tested**\n'
testFile += '\n'
testFile += ' Here discuss what variables and states are being checked. \n'
testFile += ' """\n'
testFile += f' [testResults, testMessage] = {self.moduleName}TestFunction(show_plots, param1, param2, accuracy)\n'
testFile += ' assert testResults < 1, testMessage\n'
testFile += '\n'
testFile += '\n'
testFile += f'def {self.moduleName}TestFunction(show_plots, param1, param2, accuracy):\n'
testFile += ' """Test method"""\n'
testFile += ' testFailCount = 0\n'
testFile += ' testMessages = []\n'
testFile += ' unitTaskName = "unitTask"\n'
testFile += ' unitProcessName = "TestProcess"\n'
testFile += '\n'
testFile += ' unitTestSim = SimulationBaseClass.SimBaseClass()\n'
testFile += ' testProcessRate = macros.sec2nano(0.5)\n'
testFile += ' testProc = unitTestSim.CreateNewProcess(unitProcessName)\n'
testFile += ' testProc.addTask(unitTestSim.CreateNewTask(unitTaskName, testProcessRate))\n'
testFile += '\n'
testFile += ' # setup module to be tested\n'
if type == "C++":
testFile += f' module = {self.moduleName}.{self._className}()\n'
elif type == "C":
testFile += f' module = {self.moduleName}.{self.moduleName}()\n'
else:
self.log(f"{failColor}ERROR: {endColor}Wrong module type provided to test file method.")
exit(0)
testFile += f' module.ModelTag = "{self.moduleName}Tag"\n'
testFile += ' unitTestSim.AddModelToTask(unitTaskName, module)\n'
testFile += '\n'
testFile += ' # Configure blank module input messages\n'
for msg in self.inMsgList:
testFile += f' {msg["var"]}Data = messaging.{msg["type"]}Payload()\n'
testFile += f' {msg["var"]} = messaging.{msg["type"]}().write({msg["var"]}Data)\n'
testFile += '\n'
testFile += ' # subscribe input messages to module\n'
for msg in self.inMsgList:
testFile += f' module.{msg["var"]}.subscribeTo({msg["var"]})\n'
testFile += '\n'
testFile += ' # setup output message recorder objects\n'
for msg in self.outMsgList:
testFile += f' {msg["var"]}Rec = module.{msg["var"]}.recorder()\n'
testFile += f' unitTestSim.AddModelToTask(unitTaskName, {msg["var"]}Rec)\n'
testFile += '\n'
testFile += ' unitTestSim.InitializeSimulation()\n'
testFile += ' unitTestSim.ConfigureStopTime(macros.sec2nano(1.0))\n'
testFile += ' unitTestSim.ExecuteSimulation()\n'
testFile += '\n'
testFile += ' # pull module data and make sure it is correct\n'
testFile += '\n'
testFile += ' if testFailCount == 0:\n'
testFile += ' print("PASSED: " + module.ModelTag)\n'
testFile += ' else:\n'
testFile += ' print(testMessages)\n'
testFile += '\n'
testFile += ' return [testFailCount, "".join(testMessages)]\n'
testFile += '\n'
testFile += '\n'
testFile += 'if __name__ == "__main__":\n'
testFile += f' test_{self.moduleName}(False, 1, 1, 1e-12)\n'
testFile += '\n'
testFile += '\n'
with open(testFileName, 'w') as w:
w.write(testFile)
self.log("Done")
[docs]
def createCppModule(self):
"""
Create a C++ Basilisk module
"""
modulePath = self.modulePathRelSrc
name = self.moduleName
briefDescription = self.briefDescription
inMsgList = self.inMsgList
outMsgList = self.outMsgList
variableList = self.variableList
self.log(statusColor + '\nCreating C++ Module: ' + endColor + name)
self._className = re.sub('([a-zA-Z])', lambda x: x.groups()[0].upper(), name, 1)
# read in the license information
self.readLicense()
licenseC = "/*" + self._licenseText + "*/\n\n"
# make sure the path, specified relative to basilisk/src, to the new module location is correct
self._absPath = os.path.join(pathToSrc, modulePath)
self.checkPathToNewFolderLocation()
# create new Module folder
self._newModuleLocation = os.path.join(self._absPath, name)
self.createNewModuleFolder()
#
# make module header file
#
headerFileName = name + ".h"
self.log(f"{statusColor}Creating Header File {headerFileName}:{endColor}", end=" ")
headerFile = licenseC
headerFile += '\n'
headerFile += f'#ifndef {name.upper()}_H\n'
headerFile += f'#define {name.upper()}_H\n'
headerFile += '\n'
headerFile += '#include "architecture/_GeneralModuleFiles/sys_model.h"\n'
# loop over message definition includes
includedMsgs = []
for msg in inMsgList + outMsgList:
# ensure we don't include message definition files multiple times
if msg['type'] not in includedMsgs:
if msg['wrap'] == 'C':
headerFile += f'#include "architecture/msgPayloadDefC/{msg["type"]}Payload.h"\n'
if msg['wrap'] == 'C++':
headerFile += f'#include "architecture/msgPayloadDefCpp/{msg["type"]}Payload.h"\n'
includedMsgs.append(msg['type'])
headerFile += '#include "architecture/utilities/bskLogging.h"\n'
headerFile += '#include "architecture/messaging/messaging.h"\n'
headerFile += '\n'
headerFile += f'/*! @brief {briefDescription}\n */\n'
headerFile += f'class {self._className}: public SysModel {{\n'
headerFile += 'public:\n'
headerFile += f' {self._className}();\n'
headerFile += f' ~{self._className}();\n'
headerFile += '\n'
headerFile += ' void Reset(uint64_t CurrentSimNanos);\n'
headerFile += ' void UpdateState(uint64_t CurrentSimNanos);\n'
headerFile += '\n'
headerFile += 'public:\n'
for msg in inMsgList:
headerFile += f' ReadFunctor<{msg["type"]}Payload> {msg["var"]}; //!< {msg["desc"]}\n'
headerFile += '\n'
for msg in outMsgList:
headerFile += f' Message<{msg["type"]}Payload> {msg["var"]}; //!< {msg["desc"]}\n'
headerFile += '\n'
headerFile += ' BSKLogger bskLogger; //!< BSK Logging\n'
headerFile += '\n'
if len(variableList):
for msg in variableList:
varName = msg['var'];
headerFile += f" /** setter for `{varName}` property */\n"
headerFile += f' void set{varName[:1].upper() + varName[1:]}({msg["type"]});\n'
headerFile += f" /** getter for `{varName}` property */\n"
headerFile += (f' {msg["type"]} get{varName[:1].upper() + varName[1:]}() '
f'const {{return this->{varName};}}\n')
headerFile += '\n'
headerFile += 'private:\n'
for msg in variableList:
headerFile += f' {msg["type"]} {msg["var"]}; //!< {msg["desc"]}\n'
headerFile += '\n'
headerFile += '};\n'
headerFile += '\n'
headerFile += "\n#endif\n"
with open(headerFileName, 'w') as w:
w.write(headerFile)
self.log("Done")
#
# make module definition file
#
defFileName = name + ".cpp"
self.log(statusColor + "Creating Definition File " + defFileName + ":" + endColor, end=" ")
defFile = licenseC
defFile += '\n'
defFile += f'#include "{modulePath}/{name}/{name}.h"\n'
defFile += '#include <iostream>\n'
defFile += '#include <cstring>\n'
defFile += '\n'
defFile += '/*! This is the constructor for the module class. It sets default variable\n'
defFile += ' values and initializes the various parts of the model */\n'
defFile += self._className + '::' + self._className + '()\n'
defFile += '{\n'
if self.variableList:
defFile += ' // initialize module variables\n'
for msg in variableList:
defFile += f' this->{msg["var"]} = {{}};\n'
defFile += '}\n'
defFile += '\n'
defFile += '/*! Module Destructor */\n'
defFile += f'{self._className}::~{self._className}()\n'
defFile += '{\n'
defFile += '}\n'
defFile += '\n'
defFile += '/*! This method is used to reset the module and checks that required input messages are connect.\n'
defFile += ' @return void\n'
defFile += '*/\n'
defFile += f'void {self._className}::Reset(uint64_t CurrentSimNanos)\n'
defFile += '{\n'
defFile += ' // check that required input messages are connected\n'
for msg in inMsgList:
defFile += f' if (!this->{msg["var"]}.isLinked()) {{\n'
defFile += f' bskLogger.bskLog(BSK_ERROR, "{self._className}.{msg["var"]} was not linked.");\n'
defFile += ' }\n'
defFile += '\n'
defFile += '}\n'
defFile += '\n'
defFile += '\n'
defFile += '/*! This is the main method that gets called every time the module is updated. ' \
'Provide an appropriate description.\n'
defFile += ' @return void\n'
defFile += '*/\n'
defFile += f'void {self._className}::UpdateState(uint64_t CurrentSimNanos)\n'
defFile += '{\n'
for msg in inMsgList + outMsgList:
defFile += f' {msg["type"]}Payload {msg["var"]}Buffer; //!< local copy of message buffer\n'
defFile += '\n'
defFile += ' // always zero the output message buffers before assigning values\n'
for msg in outMsgList:
defFile += f' {msg["var"]}Buffer = this->{msg["var"]}.zeroMsgPayload;\n'
defFile += '\n'
defFile += ' // read in the input messages\n'
for msg in inMsgList:
defFile += f' {msg["var"]}Buffer = this->{msg["var"]}();\n'
defFile += '\n'
defFile += ' // do some math and stuff to populate the output messages\n'
defFile += '\n'
defFile += ' // write to the output messages\n'
for msg in outMsgList:
defFile += f' this->{msg["var"]}.write(&{msg["var"]}Buffer, this->moduleID, CurrentSimNanos);\n'
defFile += '}\n'
defFile += '\n'
if variableList:
for msg in variableList:
varName = msg['var']
defFile += f'void {self._className}::set{varName[:1].upper() + varName[1:]}({msg["type"]} var)\n'
defFile += '{\n'
defFile += f' this->{varName} = var;\n'
defFile += '}\n'
defFile += '\n'
with open(defFileName, 'w') as w:
w.write(defFile)
self.log("Done")
#
# make module swig interface file
#
swigFileName = name + ".i"
self.log(statusColor + "Creating Swig Interface File " + swigFileName + ":" + endColor, end=" ")
swigFile = licenseC
swigFile += f'%module {name}\n'
swigFile += '%{\n'
swigFile += f' #include "{name}.h"\n'
swigFile += '%}\n'
swigFile += '\n'
swigFile += '%pythoncode %{\n'
swigFile += ' from Basilisk.architecture.swig_common_model import *\n'
swigFile += '%}\n'
swigFile += '%include "std_string.i"\n'
swigFile += '%include "swig_conly_data.i"\n'
swigFile += '\n'
swigFile += '%include "sys_model.i"\n'
swigFile += f'%include "{name}.h"\n'
swigFile += '\n'
includedMsgs = []
for msg in inMsgList + outMsgList:
# ensure we don't include message definition files multiple times
if msg['type'] not in includedMsgs:
if msg['wrap'] == 'C':
swigFile += f'%include "architecture/msgPayloadDefC/{msg["type"]}Payload.h"\n'
swigFile += f'struct {msg["type"]}_C;\n'
if msg['wrap'] == 'C++':
swigFile += f'%include "architecture/msgPayloadDefCpp/{msg["type"]}Payload.h"\n'
includedMsgs.append(msg['type'])
swigFile += '\n'
swigFile += '%pythoncode %{\n'
swigFile += 'import sys\n'
swigFile += 'protectAllClasses(sys.modules[__name__])\n'
swigFile += '%}\n'
swigFile += '\n'
with open(swigFileName, 'w') as w:
w.write(swigFile)
self.log("Done")
# make module definition file
self.createRstFile()
# make module unit test file
self.createTestFile("C++")
# restore current working directory
os.chdir(initialCwd)
[docs]
def createCModule(self):
"""
Create a C Basilisk module
"""
modulePath = self.modulePathRelSrc
name = self.moduleName
briefDescription = self.briefDescription
inMsgList = self.inMsgList
outMsgList = self.outMsgList
variableList = self.variableList
self.log(f"{statusColor}\nCreating C Module: {endColor}{name}")
self._className = re.sub('([a-zA-Z])', lambda x: x.groups()[0].upper(), name, 1)
# read in the license information
self.readLicense()
licenseC = f"/*{self._licenseText}*/\n\n"
# make sure the path, specified relative to basilisk/src, to the new module location is correct
self._absPath = os.path.join(pathToSrc, modulePath)
self.checkPathToNewFolderLocation()
# create new Module folder
self._newModuleLocation = os.path.join(self._absPath, name)
self.createNewModuleFolder()
#
# make module header file
#
headerFileName = f"{name}.h"
self.log(f"{statusColor}Creating Header File {headerFileName}:{endColor}", end=" ")
headerFile = licenseC
headerFile += '\n'
headerFile += f'#ifndef {name.upper()}_H\n'
headerFile += f'#define {name.upper()}_H\n'
headerFile += '\n'
headerFile += '#include <stdint.h>\n'
# loop over message definition includes
includedMsgs = []
for msg in inMsgList + outMsgList:
# ensure we don't include message definition files multiple times
if msg['type'] not in includedMsgs:
if msg['wrap'] == 'C':
headerFile += f'#include "cMsgCInterface/{msg["type"]}_C.h"\n'
if msg['wrap'] == 'C++':
self.log(f"{failColor}Error: {endColor}You can't include C++ messages in a C module.")
exit()
includedMsgs.append(msg['type'])
headerFile += '#include "architecture/utilities/bskLogging.h"\n'
headerFile += '\n'
headerFile += f'/*! @brief {briefDescription}\n */\n'
headerFile += f'typedef struct {{\n'
headerFile += '\n'
headerFile += ' /* declare module IO interfaces */\n'
for msg in inMsgList:
headerFile += f' {msg["type"]}_C {msg["var"]}; //!< {msg["desc"]}\n'
for msg in outMsgList:
headerFile += f' {msg["type"]}_C {msg["var"]}; //!< {msg["desc"]}\n'
if len(variableList):
headerFile += '\n'
for msg in variableList:
headerFile += f' {msg["type"]} {msg["var"]}; //!< {msg["desc"]}\n'
headerFile += '\n'
headerFile += ' BSKLogger *bskLogger; //!< BSK Logging\n'
headerFile += f'}}{name}Config;\n'
headerFile += '\n'
headerFile += '#ifdef __cplusplus\n'
headerFile += 'extern "C" {\n'
headerFile += '#endif\n'
headerFile += f' void SelfInit_{name}({name}Config *configData, int64_t moduleID);\n'
headerFile += f' void Update_{name}({name}Config *configData, uint64_t callTime, int64_t moduleID);\n'
headerFile += f' void Reset_{name}({name}Config *configData, uint64_t callTime, int64_t moduleID);\n'
headerFile += '\n'
headerFile += '#ifdef __cplusplus\n'
headerFile += '}\n'
headerFile += '#endif\n'
headerFile += '\n'
headerFile += '#endif\n'
with open(headerFileName, 'w') as w:
w.write(headerFile)
self.log("Done")
#
# make module definition file
#
defFileName = f"{name}.c"
self.log(f"{statusColor}Creating Definition File {defFileName}:{endColor}", end=" ")
defFile = licenseC
defFile += '\n'
defFile += f'#include "{modulePath}/{name}/{name}.h"\n'
defFile += '#include "string.h"\n'
defFile += '\n'
defFile += '/*!\n'
defFile += ' This method initializes the output messages for this module.\n'
defFile += ' @return void\n'
defFile += ' @param configData The configuration data associated with this module\n'
defFile += ' @param moduleID The module identifier\n'
defFile += ' */\n'
defFile += f'void SelfInit_{name}({name}Config *configData, int64_t moduleID)\n'
defFile += '{\n'
for msg in outMsgList:
defFile += f' {msg["type"]}_C_init(&configData->{msg["var"]});\n'
defFile += '}\n'
defFile += '\n'
defFile += '\n'
defFile += '/*! This method performs a complete reset of the module. Local module variables that retain\n'
defFile += ' time varying states between function calls are reset to their default values.\n'
defFile += ' Check if required input messages are connected.\n'
defFile += ' @return void\n'
defFile += ' @param configData The configuration data associated with the module\n'
defFile += ' @param callTime [ns] time the method is called\n'
defFile += ' @param moduleID The module identifier\n'
defFile += '*/\n'
defFile += f'void Reset_{name}({name}Config *configData, uint64_t callTime, int64_t moduleID)\n'
defFile += '{\n'
defFile += ' // check if the required message has not been connected\n'
for msg in inMsgList:
defFile += f' if (!{msg["type"]}_C_isLinked(&configData->{msg["var"]})) {{\n'
defFile += f' _bskLog(configData->bskLogger, BSK_ERROR, "Error: {name}.{msg["var"]}' \
+ ' was not connected.");\n'
defFile += ' }\n'
defFile += '}\n'
defFile += '\n'
defFile += '\n'
defFile += '/*! Add a description of what this main Update() routine does for this module\n'
defFile += ' @return void\n'
defFile += ' @param configData The configuration data associated with the module\n'
defFile += ' @param callTime The clock time at which the function was called (nanoseconds)\n'
defFile += ' @param moduleID The module identifier\n'
defFile += '*/\n'
defFile += f'void Update_{name}({name}Config *configData, uint64_t callTime, int64_t moduleID)\n'
defFile += '{\n'
for msg in inMsgList + outMsgList:
defFile += f' {msg["type"]}Payload {msg["var"]}Buffer; //!< local copy of message buffer\n'
defFile += '\n'
defFile += ' // always zero the output message buffers before assigning values\n'
for msg in outMsgList:
defFile += f' {msg["var"]}Buffer = {msg["type"]}_C_zeroMsgPayload();\n'
defFile += '\n'
defFile += ' // read in the input messages\n'
for msg in inMsgList:
defFile += f' {msg["var"]}Buffer = {msg["type"]}_C_read(&configData->{msg["var"]});\n'
defFile += '\n'
defFile += ' // do some math and stuff to populate the output messages\n'
defFile += '\n'
defFile += ' // write to the output messages\n'
for msg in outMsgList:
defFile += f' {msg["type"]}_C_write(&{msg["var"]}Buffer, &configData->{msg["var"]}, moduleID, callTime);\n'
defFile += '}\n'
defFile += '\n'
with open(defFileName, 'w') as w:
w.write(defFile)
self.log("Done")
#
# make module swig interface file
#
swigFileName = f"{name}.i"
self.log(f"{statusColor}Creating Swig Interface File {swigFileName}:{endColor}", end=" ")
swigFile = licenseC
swigFile += f'%module {name}\n'
swigFile += '%{\n'
swigFile += f' #include "{name}.h"\n'
swigFile += '%}\n'
swigFile += '\n'
swigFile += '%pythoncode %{\n'
swigFile += ' from Basilisk.architecture.swig_common_model import *\n'
swigFile += '%}\n'
swigFile += '%include "swig_c_wrap.i"\n'
swigFile += f'%c_wrap({name});\n'
swigFile += '\n'
swigFile += f'%include "{name}.h"\n'
swigFile += '\n'
includedMsgs = []
for msg in inMsgList + outMsgList:
# ensure we don't include message definition files multiple times
if msg['type'] not in includedMsgs:
if msg['wrap'] == 'C':
swigFile += f'%include "architecture/msgPayloadDefC/{msg["type"]}Payload.h"\n'
swigFile += f'struct {msg["type"]}_C;\n'
if msg['wrap'] == 'C++':
self.log(f"{failColor}ERROR: {endColor}you cannot swig a C++ message in a C module.")
includedMsgs.append(msg['type'])
swigFile += '\n'
swigFile += '%pythoncode %{\n'
swigFile += 'import sys\n'
swigFile += 'protectAllClasses(sys.modules[__name__])\n'
swigFile += '%}\n'
swigFile += '\n'
with open(swigFileName, 'w') as w:
w.write(swigFile)
self.log("Done")
# make module definition file
self.createRstFile()
# make module unit test file
self.createTestFile("C")
os.chdir(initialCwd)
[docs]
def fillCppInfo(module):
"""Fill in the C++ module information. This should be edited before running to meet the new module needs."""
# define the path where the Basilisk module folder will be
module.modulePathRelSrc = os.path.join("moduleTemplates", "")
# define module name and brief description
module.moduleName = "autoCppModule" # should be lower camel case
module.briefDescription = "This is an auto-created sample C++ module. The description is included with " \
"the module class definition"
module.copyrightHolder = "Autonomous Vehicle Systems Lab, University of Colorado Boulder"
# provide list of input messages
# leave list empty if there are no input messages
inMsgList = list()
inMsgList.append({'type': 'AttRefMsg', 'var': 'someInMsg', 'desc': 'input msg description', 'wrap': 'C'})
inMsgList.append({'type': 'AttRefMsg', 'var': 'some2InMsg', 'desc': 'input msg description', 'wrap': 'C'})
inMsgList.append({'type': 'CSSConfigMsg', 'var': 'anotherInMsg', 'desc': 'input msg description', 'wrap': 'C'})
inMsgList.append({'type': 'CSSConfigLogMsg', 'var': 'anotherCppInMsg', 'desc': 'input msg description', 'wrap': 'C++'})
module.inMsgList = inMsgList
# provide list of output messages
# leave list empty if there are no input messages
outMsgList = list()
outMsgList.append({'type': 'AttRefMsg', 'var': 'some2OutMsg', 'desc': 'output msg description', 'wrap': 'C'})
outMsgList.append({'type': 'SCStatesMsg', 'var': 'someOutMsg', 'desc': 'output msg description', 'wrap': 'C'})
outMsgList.append({'type': 'RWConfigMsg', 'var': 'anotherCppOutMsg', 'desc': 'output msg description', 'wrap': 'C++'})
module.outMsgList = outMsgList
# provide list of module variables
# leave list empty if you are not setting up module variables at this stage
variableList = list()
variableList.append({'type': 'double', 'var': 'varDouble', 'desc': '[units] variable description'})
variableList.append({'type': 'int', 'var': 'varInt', 'desc': '[units] variable description'})
module.variableList = variableList
[docs]
def fillCInfo(module):
"""Fill in the C module information. This should be edited before running to meet the new module needs."""
# define the path where the Basilisk module folder will be
module.modulePathRelSrc = os.path.join("moduleTemplates", "")
# define module name and brief description
module.moduleName = "autoCModule" # should be lower camel case
module.briefDescription = "This is an auto-created sample C module. The description is included with " \
"the module class definition"
module.copyrightHolder = "Autonomous Vehicle Systems Lab, University of Colorado Boulder"
# provide list of input messages
# leave list empty if there are no input messages
inMsgList = list()
inMsgList.append({'type': 'AttRefMsg', 'var': 'someInMsg', 'desc': 'input msg description', 'wrap': 'C'})
inMsgList.append({'type': 'AttRefMsg', 'var': 'some2InMsg', 'desc': 'input msg description', 'wrap': 'C'})
inMsgList.append({'type': 'CSSConfigMsg', 'var': 'anotherInMsg', 'desc': 'input msg description', 'wrap': 'C'})
module.inMsgList = inMsgList
# provide list of output messages
# leave list empty if there are no input messages
outMsgList = list()
outMsgList.append({'type': 'AttRefMsg', 'var': 'some2OutMsg', 'desc': 'output msg description', 'wrap': 'C'})
outMsgList.append({'type': 'SCStatesMsg', 'var': 'someOutMsg', 'desc': 'output msg description', 'wrap': 'C'})
module.outMsgList = outMsgList
# provide list of module variables
# leave list empty if you are not setting up module variables at this stage
variableList = list()
variableList.append({'type': 'double', 'var': 'varDouble', 'desc': '[units] variable description'})
variableList.append({'type': 'int', 'var': 'varInt', 'desc': '[units] variable description'})
module.variableList = variableList
if __name__ == "__main__":
makeModule = moduleGenerator()
fillCppInfo(makeModule)
makeModule.createCppModule()
fillCInfo(makeModule)
makeModule.createCModule()