Developing Safety Application

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.

pacmananimation

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.

pacman_design

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.

pacman_files

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.

pacman_left

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