Check VVM-Mappings with B&Rs Unit-Test Framework

Hi everyone,

If you’re using VVM-Mappings between your software modules, you probably appreciate that source variables are optional. However, you may have also encountered the downside: missing or inconsistent mappings can go unnoticed, and a simple compiler warning could have saved you valuable debugging time.

We’ve run into this issue multiple times, especially when making type or name changes in interface variables. Existing mappings silently stop working, with no clear warning—just a small red “x” in the mapping, visible only if you inspect the VVM file at runtime.

To my knowledge, there’s no built-in way in Automation Studio to check mappings at compile time. However, you can use the function blocks AsIOFListDP and AsIOPVInfo to detect inconsistent mappings at runtime.

To automate this process, we decided to leverage the B&R Unit Test Framework to validate VVM-Mappings between AppModules. Essentially, it’s a basic integration test:

  • The test collects all inconsistent mappings in an array and raises an assertion for each one.
  • It runs locally in ArSim on any developer’s machine.
  • Since we’ve integrated unit testing into our build pipeline, the test also runs on every pull request before merging.

Our build pipeline runs in Bitbucket on a self-hosted runner, and the results are presented as a Code Insight report:

Below is a basic version of the test. Feel free to take it as a reference—no guarantees of functionality or completeness!

_TEST PvMapping(void) {
    AsIOFListDP_0.flagsValue = 0b0000000100000000; // Setting bit 8        - 256
    AsIOFListDP_0.flagsMask  = 0b0000001100000000; // Masking bits 8 and 9 - 768
    AsIOFListDP_0.pDatapoint = 0;
    getNextDP                = 1;
    count                    = 0;

    while (getNextDP) {
        AsIOFListDP_0.enable         = 1;
        AsIOFListDP_0.pLastDatapoint = AsIOFListDP_0.pDatapoint;
        AsIOFListDP(&AsIOFListDP_0);

        if (AsIOFListDP_0.status == ERR_OK) {
            if (AsIOFListDP_0.pDatapoint != 0) {
                AsIOPVInfo_0.enable     = 1;
                AsIOPVInfo_0.pDatapoint = AsIOFListDP_0.pDatapoint;
                AsIOPVInfo(&AsIOPVInfo_0);

                if (AsIOPVInfo_0.status == ERR_OK) {
                    if (strcmp("::unknown", (void*)AsIOPVInfo_0.bufferA) != 0) {
                        memcpy(failedPVSrc[count], AsIOPVInfo_0.bufferA, sizeof(AsIOPVInfo_0.bufferA));
                        memcpy(failedPVDst[count], AsIOFListDP_0.buffer, sizeof(AsIOFListDP_0.buffer));
                        failedPVFlags[count] = AsIOPVInfo_0.flags;
                        count++;
                    }
                    getNextDP = 1;
                } else if (AsIOPVInfo_0.status != ERR_FUB_BUSY) {
                    TEST_ABORT_MSG("Error calling AsIOPVInfo");
                }

            } else {
                getNextDP = 0;
            }
        } else if (AsIOFListDP_0.status != ERR_FUB_BUSY) {
            TEST_ABORT_MSG("Error calling AsIOFListDP");
        }
    }

    for (i = 0; i < count; i++) {
        char errorMessage[255];
        errorMessage[0] = '\0';
        strcpy(errorMessage, "Invalid PV-mapping: ");
        strcat(errorMessage, failedPVSrc[i]);
        strcat(errorMessage, " >>> ");
        strcat(errorMessage, failedPVDst[i]);
        if (failedPVFlags[i] & 0x10) { //10000
            strcat(errorMessage, " | TYPE_MISMATCH");
        } else if (failedPVFlags[i] & 0x20) { //100000
            strcat(errorMessage, " | PV_NOT_FOUND");
        }
        TEST_FAIL(errorMessage);
    }
    TEST_DONE;
}

P.S. If there’s broader interest in other types of checks that can be integrated into pipelines using third-party tools, I’m happy to share more behind-the-scenes insights like this.

6 Likes

Even though I am one of many who only have a rough idea of what it actually does, I believe there are a few people in the community who will appreciate it. If you have more tips like this, not just about unit testing, I would be happy if you shared them with us! :slight_smile: