Modules in Python#

Python modules are a good alternative to native C++ modules for quick prototyping. They are defined entirely in a Python script, which means that there is no need for a header (.h), definition (.cpp), or SWIG interface file (.i). However, they are much slower than native modules, which will significantly slow down your simulation.

Python modules are implemented by subclassing SysModel from Xmera.architecture.sysModel. Then, one can implement the __init__, reset, and updateState methods in the same way that one would implement these methods in C++. Remember to always call __init__ of the parent class SysModel if you are implementing your own __init__.

The modelTag value of these python BSK modules will be a unique positive number like any other BSK module.

All Python modules have a logger stored in bskLogger (although it will not be available until the module has been added to a simulation). Additionally, you may declare any other variables, methods, messages, etc. within your Python module.

The script below expands on the code shown in Adding Xmera Modules to include a Python module.

 1    """
 2
 3    #  Create a sim module as an empty container
 4    scSim = SimulationBaseClass.SimBaseClass()
 5
 6    #  create the simulation process
 7    dynProcess = scSim.CreateNewProcess("dynamicsProcess")
 8
 9    # create the dynamics task and specify the integration update time
10    dynProcess.addTask(scSim.CreateNewTask("dynamicsTask", macros.sec2nano(5.0)))
11
12    # create copies of the xmera modules
13    mod1 = cppModuleTemplate.CppModuleTemplate()
14    mod1.modelTag = "module1"
15    scSim.AddModelToTask("dynamicsTask", mod1, 0)
16
17    mod2 = cppModuleTemplate.CppModuleTemplate()
18    mod2.modelTag = "module2"
19    scSim.AddModelToTask("dynamicsTask", mod2, 5)
20
21    mod3 = cppModuleTemplate.CppModuleTemplate()
22    mod3.modelTag = "module3"
23    scSim.AddModelToTask("dynamicsTask", mod3, 15)
24
25    # The following is a Python module, which has a higher priority
26    # then some of the C++/C modules. Observe in the script output
27    # how the Python module is called in the order that respects
28    # its priority with respect to the rest of the modules.
29    mod4 = TestPythonModule()
30    mod4.modelTag = "pythonModule4"
31    scSim.AddModelToTask("dynamicsTask", mod4, 10)
32
33    mod2.dataInMsg.subscribeTo(mod4.dataOutMsg)
34    mod4.dataInMsg.subscribeTo(mod3.dataOutMsg)
35
36    # Set up recording
37    mod2MsgRecorder = mod2.dataOutMsg.recorder()
38    scSim.AddModelToTask("dynamicsTask", mod2MsgRecorder)
39
40    #  initialize Simulation:
41    scSim.InitializeSimulation()
42    print("InitializeSimulation() completed...")
43
44    #   configure a simulation stop time and execute the simulation run
45    scSim.ConfigureStopTime(macros.sec2nano(5.0))
46    scSim.ExecuteSimulation()
47
48    print("Recorded mod2.dataOutMsg.dataVector: ", mod2MsgRecorder.dataVector)
49
50    return
51
52
53class TestPythonModule(sim_model.SysModel):
54    def __init__(self, *args):
55        super().__init__(*args)
56        self.dataInMsg = messaging.ModuleTemplateMsgReader()
57        self.dataOutMsg = messaging.ModuleTemplateMsg()
58
59    def reset(self, currentSimNanos):
60        # Ensure that self.dataInMsg is linked
61        if not self.dataInMsg.isLinked():
62            self.bskLogger.bskLog(
63                sim_model.BSK_ERROR, "TestPythonModule.dataInMsg is not linked."
64            )
65
66        # Initialiazing self.dataOutMsg
67        payload = messaging.ModuleTemplateMsgPayload()
68        payload.dataVector = np.array([0, 0, 0])
69        self.dataOutMsg.write(payload, currentSimNanos, self.moduleID)
70
71        self.bskLogger.bskLog(sim_model.BSK_INFORMATION, "Reset in TestPythonModule")
72
73    def updateState(self, currentSimNanos):
74        # Read input message
75        inPayload = self.dataInMsg()
76        inputVector = inPayload.dataVector
77
78        # Set output message
79        payload = messaging.ModuleTemplateMsgPayload()
80        payload.dataVector = (
81            self.dataOutMsg.read().dataVector + np.array([0, 1, 0]) + inputVector
82        )
83        self.dataOutMsg.write(payload, currentSimNanos, self.moduleID)
84
85        self.bskLogger.bskLog(
86            sim_model.BSK_INFORMATION,
87            f"Python Module ID {self.moduleID} ran Update at {currentSimNanos*1e-9}s",
88        )
89
90
91if __name__ == "__main__":
92    run()

Running the above code prints:

(.venv) source/code-samples % python making-pyModules.py
BSK_INFORMATION: Variable dummy set to 0.000000 in reset.
BSK_INFORMATION: Reset in TestPythonModule
BSK_INFORMATION: Variable dummy set to 0.000000 in reset.
BSK_INFORMATION: Variable dummy set to 0.000000 in reset.
InitializeSimulation() completed...
BSK_INFORMATION: Module ID 3 ran Update at 0.000000s
BSK_INFORMATION: Python Module ID 4 ran Update at 0.0s
BSK_INFORMATION: Module ID 2 ran Update at 0.000000s
BSK_INFORMATION: Module ID 1 ran Update at 0.000000s
BSK_INFORMATION: Module ID 3 ran Update at 5.000000s
BSK_INFORMATION: Python Module ID 4 ran Update at 5.0s
BSK_INFORMATION: Module ID 2 ran Update at 5.000000s
BSK_INFORMATION: Module ID 1 ran Update at 5.000000s
Recorded mod2.dataOutMsg.dataVector:  [[2. 1. 0.]
[5. 2. 0.]]

Note how the Python module made use of bskLogger, the reset and updateState were called, how the priority of the Python module was respected, and how messaging happened between a C++ and Python module.

The scenario scenarioAttitudePointingPy further shows how to define Python modules.