C

Monitor: Verifying the Rendering Output

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

// This file is part of the Qt Safe Renderer module
#include <QThread>
#include <QDebug>
#include <signal.h>
#include <string.h>

#include <QtSafeRenderer/qsafeglobal.h>
// We must use the SafeRenderer namespace here because
// the Monitor library is written in C, and its types
// are implemented without a namespace.
using namespace SafeRenderer;

extern "C" {
#include <QtSafeRenderer/qsafeglobal.h>
#include <QtSafeMonitor/message.h>
#include <QtSafeMonitor/eventProcessor.h>
#include <QtSafeMonitor/errorcode.h>
#include <QtSafeMonitor/message.h>
#include <QtSafeMonitor/endian.h>
#include <QtSafeMonitor/qsafechecksum.h>
#include <QtSafeMonitor/crccache.h>
#include <QtSafeMonitor/monitorconfig.h>
#include "controller.h"
#include "sendevent.h"
#include "goldencrccalculator.h"
}

#define MONITOR_MSG_BUF_SIZE 128U
static volatile sig_atomic_t g_running = 1;
static quint32 g_errorCalled = 0;
static void ctrlCHandler(qint32 receivedSignal)
{
    if (receivedSignal == SIGINT) {
        g_running = 0;
    } else {
        /* Nothing */
    }
}

static void defaultErrorHandler(ErrorCode errorType, qint64 arg1, qint64 arg2)
{
    static qchar msgBuf[MONITOR_MSG_BUF_SIZE];
    g_errorCalled = 1U;
    showInfoPanelOnError(1, msgBuf, sizeof(msgBuf));

    qWarning() << "Error:" << errorCodeToString(errorType);
    qWarning() << "Error arguments:" <<  arg1 << "," << arg2;
}

static void requestOutpuVerificationUpdate(qchar *msgBuf, const size_t len, errorFunc errorHandler)
{
    for (quint32 i = 0; i < itemsCount(); i++) {
        const quint32 itemId = getItemIdAtIndex(i);
        if (getEventOutputVerificationVerifyItem(itemId, msgBuf, len) != NULL) {
            if (sendEvent(msgBuf, len) < 0) {
                errorHandler(FailedToSendRequest, 0, __LINE__);
                break;
            }
        }
    }
}

static qint32 verifyEventOutput(qchar *msgBuf, const size_t len, errorFunc errorHandler)
{
    qint32 ret = -1;
    qchar replyBuffer[MONITOR_MSG_BUF_SIZE];

    getEventOutputVerificationRequest(msgBuf, len);

    // Send the event and then receive a reply. Use sendEventWithReply for this.
    if (sendEventWithReply(msgBuf, len, msgBuf, len) == 0) {
        // Successful reply received.
        ret = processEvent(msgBuf, len, errorHandler);
    } else {
        errorHandler(FailedToReceiveMessage, (qint64)ret, __LINE__);
    }

    return ret;
}

static void monitorLoop(void)
{
    qchar msgBuf[MONITOR_MSG_BUF_SIZE];
    qint32 ret = 0;
    quint32 speedTextExpectedCRC = 0U;
    const quint32 speedTextItemId = qsafe_hash("safetextitem", strlen("safetextitem"));

    /* Read the layout for mirroring */
    SafeLayoutHandle layoutHandle = qsrCreateLayoutFromResource("/layoutData/MainForm/MainForm.ui.srl");
    GoldenCrcCalculatorHandle crcCalculator = qsrGoldenCrcCalculatorCreate(layoutHandle);

    while (g_running > 0) {
        g_errorCalled = 0U;
        // Request renderer to read the CRC values of static items
        requestOutpuVerificationUpdate(msgBuf, sizeof(msgBuf), &defaultErrorHandler);
        // Request renderer to read the dynamic text
        getEventOutputVerificationVerifyItem(speedTextItemId, msgBuf, MONITOR_MSG_BUF_SIZE);
        sendEvent(msgBuf, MONITOR_MSG_BUF_SIZE);

        // Wait for to VSync period to settle
        QThread::msleep(100);

        // Read back the actual CRC values from renderer
        do {
            ret = verifyEventOutput(msgBuf, sizeof(msgBuf), &defaultErrorHandler);
        } while (ret > 0);

        // Tick the simulator to change the states
        advanceState(msgBuf, sizeof(msgBuf), &defaultErrorHandler);

        // Calculate the dynamic text Golden CRC value
        const qchar* speedTextStr = getCurrentSpeedText();
        qsrSetTextForItem(speedTextItemId, speedTextStr, layoutHandle);
        qsrCalculateItemCrc(crcCalculator, speedTextItemId, 0U, &speedTextExpectedCRC);
        // Set the CRC value to cache
        crcCache_setCRC(speedTextItemId, speedTextExpectedCRC);

        // Delay to have 100 msec loop
        QThread::msleep(100);

        // Clear the info panel if errors are cleared
        if (g_errorCalled == 0U) {
            showInfoPanelOnError(0, msgBuf, sizeof(msgBuf));
        }
    }
}

qint32 main(qint32 argc, char *argv[])
{
    qint32 c = 0;
    qint32 ret = 0;

    if (signal(SIGINT, &ctrlCHandler) == SIG_ERR) {
        ret = 3;
    } else {
        monitorLoop();
    }
    return ret;
}