-
-
Notifications
You must be signed in to change notification settings - Fork 7
Overview of the Meta Model
ROSMOD is an extension and formalization of ROS which extends the scheduling layer, formalizes a component model with proper execution semantics, and adds modeling of software and systems for a full tool-suite according to model-driven engineering.
When developing models using ROSMOD, the top-level entity is a ROSMOD Project
, which is a self-contained collection of
- Software
- Systems
- Deployments
- Experiments
These collections act as folders to categorize and separate the many different model objects by their associated modeling concept. In this way, the software defined for a project can be kept completely separate from any of the systems on which the software may run. Similarly, the different ways the software may be instantiated and collocated at run-time is separated into specific deployments which are independent of the hardware model and to some degree the software model.
To create a new project, you can either drag and drop a Project
object from the Part Browser
into the canvas when viewing the Projects
root node or you can right click on the Projects
root node of the Tree Browser
and create a new child of type Project
. Please note that you cannot drag a Documentation
object into the Projects
canvas to create a documentation object and that if you choose to create a Documentation
object inside the Projects
root node it will not be displayed in the RootViz
visualizer which shows all the Projects and will not be included in any of the generated documentation.
Each project has special Code Documentation
attributes: Authors
, Brief Description
, and Detailed Description
, which are best edited by clicking on the CodeEditor
visualizer. This visualizer fills the canvas with a CodeMirror instance which allows the user to easily edit multi-line strings with:
- automatic saving when changes are made
- undo/redo
- syntax highlighting
- code completion, activated with
ctrl+space
- code folding, using the gutter buttons or
ctrl+q
on the top of the code to be folded (e.g. start of anif
block)
while allowing the user to configure (using drop-down menus):
- the currently viewed/edited attribute
- the current color theme of the code editor
- the current keybindings associated with the code editor (supported keybindings are
sublime
,emacs
, andvim
)
The CodeEditor
visualizer is used in many places throughout the UI; any object that has attributes which support editing using the CodeEditor
will display the CodeEditor
as a selection in the visualizers list in the Visualizer Panel
.
While viewing a project, the user can run the following plugins:
-
SoftwareGenerator: for generating and optionally compiling the
software
defined for the project according to thehost architectures
defined in thesystem models
of the project. -
GenerateDocumenation: aggregates all the
Documentation
objects in the project's tree, converts them toReStructuredText
and compiles them intohtml
andpdf
. -
TimingAnalysis: generates a
Colored Petri-Net
model for performing timing analysis on thedeployments
(software instanced on abstract hardware)
A ROSMOD package contains the definitions for its associated Messages
and Services
, which follow the ROS definitions, as well as the definitions for ROSMOD Components
.
Messages contain a definition
attribute (editable using a CodeMirror dialog). This definition attribute conforms to the ROS Message Description Specification. Messages allow components to interact using Publishers
and Subscribers
, through a non-blocking, one-to-many publish/subscribe interaction pattern. Non-blocking means that when a component publishes a message, the publish returns immediately, without waiting for any or all subscribers to acknowledge that they have received the message.
Here is an example Message definition
:
int32 X=123
int32 Y=-123
string FOO="this is a constant"
string EXAMPLE="this is another constant"
The definition
is edited using the CodeEditor
visualizer, as described in the beginning of this sample's documentation. Since a Message
has no other valid visualizers, when you double-click on a message, it will automatically open into its definition to be viewed/edited using the CodeEditor
visualizer.
Services contain a definition
attribute (editable using a CodeMirror dialog). This definition attribute conforms to the ROS Service Description Specification. Services allow components to interact using Clients
and Servers
, through a blocking, one-to-one client/server interaction pattern. Blocking means that the component that issues the client call to the server must wait and cannot execute other code until it receives the response from the server.
Here is an example Service definition
:
#request constants
int8 FOO=1
int8 BAR=2
#request fields
int8 foobar
another_pkg/AnotherMessage msg
---
#response constants
uint32 SECRET=123456
#response fields
another_pkg/YetAnotherMessage val
CustomMessageDefinedInThisPackage value
uint32 an_integer
The definition
is edited using the CodeEditor
visualizer, as described in the beginning of this sample's documentation. Since a Service
has no other valid visualizers, when you double-click on a message, it will automatically open into its definition to be viewed/edited using the CodeEditor
visualizer.
Components are single threaded actors which communicate with other components using the publish/subscribe and client/server interaction patterns. These interactions trigger operations to fire in the components, where the operation is a function implemented by the user inside the operation
attribute of the relevant subscriber
or server
. The component can also have timer operations which fire either sporadically or periodically and similarly have an operation
attribute in which the user specifies the c++ code to be run when the operation executes. These operations happen serially through the operation queue and are not preemptable by other operations of that component. Inside these operations, publisher
or client
objects can be used to trigger operations on components which have associated and connected servers
or subscribers
. These publisher
, subscriber
, client
, server
, and timer
objects are added by the user and defined inside the component.
Components contain forwards
, members
, definitions
, initialization
, and destruction
attributes which provide an interface for the user to add their own C++ code
to the component. Each of those attributes is (as previously) editable using the CodeEditor
visualizer.
-
Forwards
corresponds to code that comes before the class declaration in the generatedcomponent header file
, e.g
#include <stdio.h>
or user-created structure or class definitions.
-
Members
corresponds to private members and methods that the user wishes to add to the component in the generatedcomponent header file
. -
Definitions
corresponds to function definitions or other code that the user wishes to add to the generatedcomponent source file
. -
Initialization
corresponds to the code the user wishes to run when the component starts up to initialize members to specific values or begin the process of triggering other components in the system through publish or client interactions. -
Destruction
allows the user to specify destruction of any objects they have allocated on the heap that need to be manually destructed during component destruction.
Additionally, components contain User Configuration
and User Artifacts
attributes, which are editable as JSON
code using the CodeEditor
visualizer.
-
User Configuration
is a way of specifying the options passed to the component at run-time. The configuration object will be stored as a (possibly nested) dictionary within the component's config, atconfig["User Configuration"]
. These data are used instead of command line arguments to allow the user to send complex/nested data structures as configuration to the component, allow multiple component (instance) within a process to be configured with different values for the same parameter, and to save developer time by automatically parsing these data into usable structures. For example, given the following config:
{
"logSensorData": true,
"logPeriod": 0.1,
"logFields": {
"time": "float",
"data": "int"
}
"sensorOffsets": [
0.1,
0.2,
0.3
]
}
The user could access the configuration structure using the following c++
code anywhere within the component:
bool logData = config["User Configuration"]["logSensorData"].asBool();
float period = config["User Configuration"]["logPeriod"].asFloat();
std::string firstField = config["User Configuration"]["logFields"]["time"].asString();
std::string secondField = config["User Configuration"]["logFields"]["data"].asString();
float firstOffset = config["User Configuration"]["sensorOffsets"][0].asFloat();
float secondOffset = config["User Configuration"]["sensorOffsets"][1].asFloat();
The documentation for the generated objects can be found at jsoncpp, which is the library used to parse the JSON
.
-
User Artifacts
is a way for the user to specify any files that may be produced by the component so that the experiment / plotting infrastructure can manage and version them. The attribute is specified as aJSON array
ofstrings
where each string is a filename of an output file.
Note that both the User Artifacts
and User Configuration
can be overridden independently within any Component Instance
in a Deployment
.
Inside the component, the user can define the interaction ports the component supports (i.e. any publisher
, subscriber
, client
, and server
objects), by dragging and dropping them into the component's canvas. The relevant Message
or Service
pointers for these objects can be defined by either creating the port by dragging the relevant Message
or Service
object from the Tree Browser
into the canvas and selecting the appropriate port type from the pop-up dialog or by dragging the Message
or Service
object onto the relevant Pointer
of the already created port. Alternatively, the Message
or Service
object can be dragged on to the port in the canvas.
For Subscribers
, Servers
, and Timers
, you can edit the Operation
which gets executed on behalf of the object by double clicking on the object to open a CodeEditor
with the operation code.
Components are provided with an easy to use timestamped logging framework through their built-in logger
object pointer. The logger
object maintains a file buffer for the component which is independent of all other component logs in the system. The user can use the logger within any component code as such:
logger->log(<message name>, <message format string>, ...);
similarly to printf
, e.g.
this->altitude = 100.0f;
this->pitch = 50.0f;
this->roll = 15.0f;
this->yaw = 20.0f;
logger->log("Sensed current altitude and attitude!");
logger->log("Altitude", "%f", this->altitude);
logger->log("Pitch", "%f", this->pitch);
logger->log("Roll", "%f", this->roll);
logger->log("Yaw", "%f", this->yaw);
In this case, both Altitude and Orientation would be in the same log file (since they were in the same component) but would show up as:
ROSMOD::<timestamp 1>::Sensed Current altitude and Attitude
ROSMOD::<timestamp 2>::Altitude::100.0
ROSMOD::<timestamp 3>::Pitch::50.0
ROSMOD::<timestamp 4>::Roll::15.0
ROSMOD::<timestamp 5>::Yaw::20.0
Most importantly, any data logged will be automatically plotted in the Results Visualizer
after running an Experiment
. Any data that can be parsed numerically (e.g. the Altitude, Pitch, Roll, Yaw values above), will be plotted according to their timestamps. Any data that cannot be parsed numerically, e.g. the first textual log, will be added to the plot as an annotation that the user can click to toggle the display of the text.
Inside this aspect is where the user can specify the c++ code that will execute upon the expiry of the relevant timer
, or when relevant data is received for a subscriber
or server
. The attributes for the ports and timers can be specified in this aspect as well. These attributes include the period
of the timer
or the deadline
of the subscriber operation, for instance.
Also inside this aspect is where the user can select the Set Editor
visualizer, which allows the user to see or configure the set of Libraries that the component requires for compilation/execution. The user can drag a Source Library
or System Library
from the Tree Browser
to into the Libraries
Set Editor to add the library as a requirement for the component.
The user can drag in constraints
from the Part Browser
and name them accordingly to specify that the component must be deployed onto a Host
which has a Capability
with a name that matches the constraint's name.
In a System
model, you define Hosts
, Users
, Networks
, and Links
:
The Hosts
which are computers with a specified OS, Architecture, Device ID, and Device ID Command. The Device ID allows the user to delineate between two different devices (e.g. a BeagleBone Black and an NVIDIA Jetson TK1) which may have the same Architecture (e.g. armv7l
), but may need to be separated for binary/library incompatibility. Because these devices may have different subsystems, we allow the user to specify the Device ID Command which is run on the host and should return a string containing the specified Device ID. Hosts can have any number of Interface
children, which are displayed as ports
on the Host
.
The Users
which have an associated SSH Key location and Directory. The user's Directory is where any processes started during experiment deployment or compilation on behalf of the user will be run and where any artifacts will be generated.
A Network defines the Subnet of IP addresses available as well as the Netmask which, together with the Subnet defines the range of available IP addresses.
Links connect a hosts Interface
to a Network
and has an associated IP address that the interface will have on that network. The user can create links and assign the IP addresses by clicking and dragging from the host's interface to a network. The IP address is displayed on the link and can be edited by clicking on the Link and editing its IP property in the Property Panel
.
A Deployment can have any number of Containers
, which contain Node
processes and will execute on separate hardware from each other. In this way, a Container
is an abstract notion of a Host
.
If you click on the CommViz
Visualizer, you can see how all of the component instances, grouped within nodes (processes) are communicating between the containers. Clicking on an element (message, service, component instance, etc.) will focus that element and show the neighborhood of that element; i.e. it will show components communicating with the focused message or service, or it can show the messages / services that the focused component uses to communicate. Directions on the connections indicate whether the communication is outbound from the component (publisher/client) or inbound (subscriber/server)
An experiment maps a Deployment
's abstract grouping of component instances into processes and finally into Containers
to Hosts
from the selected System
.
The mapping and experiment deployment is performed automatically by the RunExperiment
plugin based on which Hosts
are reachable and not busy (i.e. not running any other experiment or compilation processes) from the selected system, and also ensures that the hosts satisfy all of the constraints from all of the component instances in a container's nodes. If there are not enough hosts for the number of containers or if the constraints of the software cannot be satisfied by the available hosts, the RunExperiment
plugin informs the user, else the plugin finishes the deployment and saves the mapping into the model for reference and showing to the user.
If an experiment is currently running (based on the existence of model objects corresponding to the mapping of containers to hosts), the StopExperiment
plugin will stop all the associated experiment processes and copy back all the components' generated logs. The logs are saved onto the server file system and their contents are also copied into attributes of an automatically created Results
object, whose name will be the current time at which the experiment finished. If the user opens the Results
object and selects the ResultsViz
visualizer, any tracing logs that were recovered will be automatically plotted in the canvas.