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.