Developing Safety Application
September 16, 2022 by Teemu Holappa | Comments
We initially developed Qt Safe Renderer to show warning indicators in problem situations, such as car dashboards. Along the way, we have added features, and with the new upcoming 2.0 release, you can create even more complex user interfaces for safety-critical applications.
Since making safety-critical software is usually a serious business, we decided to have fun during the traditional Qt Company's Hackathon days. We made a little game with Qt Safe Render. The purpose of the demo was to test the upcoming 2.0 release and try out how end users can create applications with a safe render.
We designed the PacMan game UI using the Qt Design Studio. There are three safe QML types that you can use for creating the safe application: SafeText, SafePicture, and SafeImage. Game elements consist of SafeImage types. We use the dynamic SafeText component to show the score value. We did not need to use the SafePicture feature. SafePicture is typically used to display warning indicators. Qt Design Studio provides the complete ISO 7000 Graphical Symbol collection.
Creating the User Interface
We made the game score component using the dynamic SafeText element. In version 2.0, we have added a new optional property called fillColor to the safety elements. Then your safety elements will always be visible regardless of the background color. The Safe Layout Tool warns if the color and fillColor contrast is too low based on Web Content Accessibility Guidelines.
SafeText {
id: points
color: "white"
text: "0"
font.pixelSize: 64
height: 128
width: 128
fillColor: "darkgray"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
runtimeEditable: true
font.family: "QtBidiTestFont"
}
We implemented other components in the game with the SafeImage type. In QSR 2.0, we support image source changing using the QML states. For example, Pacman has different source images based on the direction it is moving. You can also hide the item by changing the visible property in the PropertyChanges.
SafeImage {
id: pacman
x:0
y:0
width:60
height:60
source: "images/pacman3.png"
opacity: 1
fillColor: "black"
states: [
State {
name: "right"
PropertyChanges {
target: pacman
source: "images/pacman2.png"
}
},
State {
name: "left"
PropertyChanges {
target: pacman
source: "images/pacman2_left.png"
}
},
State {
name: "up"
PropertyChanges {
target: pacman
source: "images/pacman2_up.png"
}
},
State {
name: "down"
PropertyChanges {
target: pacman
source: "images/pacman2_down.png"
}
},
State {
name: "hide"
PropertyChanges {
target: pacman
opacity: 0
}
}
]
}
Developing the Application
Qt Safe Layouttool generates the safety content from the QML input. Safety content is the Safe Renderer-specific binary format. Bitmaps are stored in the RLE (Run-length encoding) compressed binary (.srb). The true type fonts are converted to a Qt Prerendered Font (QPF2) format and the text metadata is stored in a .srt file. The states and transition data are iterated on build time and stored in the .sts files. The layout data is stored in the .srl file.
In QSR 2.0 we provide a new feature where the Safe Layout Tool packs the generated data into the resource file automatically. In your safety application, you need to load the layout file, and QSafeLayout class reads and validates the data asset. You can have multiple layout files. For example, in this game demo, we can have several game maps.
static QSafeLayoutResourceReader firstMap("/layoutData/GameArea/GameArea1.srl");
firstMap.setLayoutId(qsafe_hash_string("map1"));
static QSafeLayoutResourceReader secondMap("/layoutData/GameArea/GameArea2.srl");
secondMap.setLayoutId(qsafe_hash_string("map2"));
Controlling the Pacman
The Player needs to move the Pacman. For UI control purposes, Safe Render provides an event interface called QSafeEvent. QSafeEvent is the base class, and it is a fixed-length byte array. The base class encodes and decodes the data to/from a byte array in the correct endianness.
There are several options for how to send these events to your safety application. For example, you can send error messages from an external processor or state changes from the main user interface.
In version 2.0, we have introduced a new API, QSafeEventSender, which makes it possible to safely send QSafeEvents between processes and threads. QSafeEventSender sends a message using the message queue interface of the operating system and has an implementation for QNX, INTEGRITY, POSIX, and TCP-socket interface.
In this example, we sent the control events from Python. The key events are received from the Pyside6 Qt Widget, converted to a QSafeEvent, and then sent through TCP/IP socket.
def createKeyEvents(key):
# The event id's are defined in qsafeevent.h
bytes = EventId.SYSTEMSTATECHANGE.to_bytes(4, byteorder='big', signed=False)
bytes += key.to_bytes(4, byteorder='big', signed=False)
return bytes.ljust(128, b"\x00")
def keyReleaseEvent(self, e):
if e.key() == Qt.Key_Left:
keyevent = createKeyEvents(0)
sendMessage(keyevent)
Game Logic
Finally, we have to clue the key events to a Pacman movement. The key events are sent from the Python script and packed to the QSafeEventSystem-class. Saferenderer provides the API for the application to hook to the event system using the event filter callback function:
enum Direction {
Left, Right, Up, Down, Stop
};
Direction g_playerDirection;
static bool filterEvents(const QSafeEvent &event)
{
if (event.getEventId() == EventSystemStateChange) {
QSafeEventSystem keyEvent(event);
quint32 key = keyEvent.systemEventId();
g_playerDirection = static_cast<Direction>(key);
return true;
}
EventHandler msgHandler(stateManager, telltaleWindow);
msgHandler.installEventFilter(&filterEvents);
Then based on the key event we can change the Pacman state changes by using the QSafeEventChangeState.
QSafeEventChangeState pacmanState;
pacmanState.setItemId(qsafe_hash_string("pacman"));
pacmanState.setStateId(qsafe_hash_string("left"));
In the "left"-state the sprite image is changed.
We can move the Pacman with the QSafeEventPosition event.
QSafeEventPosition newPosition;
QSafePoint nextPos = getDirection(monster, index);
newPosition.setItemId(monster.id());
newPosition.setX(nextPos.x());
newPosition.setY(nextPos.y());
In QSR 2.0 we have introduced a new API StateManager::getItemAtPosition. It returns the ID of the topmost item in the given position. In this demo, the API is used for collision detection. You can use this API for other purposes like touch handling to find the pressed item.
const quint32 itemId = g_stateManager->getItemAtPosition(x, y);
const LayoutData &item = g_layoutPtr->getLayoutItem(itemId);
if (item.id() == qsafe_hash_string("monster")) {
That's all Folks!
As you can see, writing safety applications using the safe renderer is pretty straightforward. We continue to add support for more QML types in our Qt Safelayout tool in coming releases, but already, there are quite a lot of things you can do with our ASIL-D pre-certified tooling and renderer. Qt Safe Renderer version 2.0 release candidate is already available.
You can find more information in the Safety Manual. If you want to know more about Safe Renderer, please Contact us.
Blog Topics:
Comments
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.8 Now!
Download the latest release here: www.qt.io/download.
Qt 6.8 release focuses on technology trends like spatial computing & XR, complex data visualization in 2D & 3D, and ARM-based development for desktop.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.