Issues with using codegen on handle objects with complex methods

9 views (last 30 days)
I'm running into a fundamental issue with using codegen in my project, and it is unclear if there is a reasonable workaround. I am using several custom handle objects within a ros2 node that are used as inputs to subscription callback functions. In order to have codegen convert all of the internal functions (ie, to fully excercise it), I need to send that node a rather long and complex series of ros2 messages that trigger these various callbacks. In order to generate these messages within the node function itself, I would have to bring in the entire large test script and go through codegen for the whole thing, even though it is irrelevant to the function of the node itself. I addition to this, many of the functions I would need to be able to do so are not supported by codegen, so I would need to rework that entire script to account for that.
What I have tried so far as a workaround is the following:
  1. Place the node generation function within the test script, and output the node and subscriber handles back into the script. This works as intended within normal matlab, but is not supported within codegen since it does not support handle outputs.
  2. If I do not output the node and subscription handle, they are deleted when the function completes. In this case the script runs but none of the callbacks I want to trigger ever get run with the data they need to be thoroughly exercised, since they are triggered by the test script but the handles to which they are associated have been deleted. I can run codegen on this version, but it ultimately runs into the following error after all other errors have been resolved: 'The library 'ROS 2 Node' is not supported by the current target, language or compiler.'
  3. If I add a loop within the function to stop it from completing, it blocks further evaluation of the test script, so the messages never get sent and the callbacks never trigger. I have not tried running codegen on this variation.
At a high level, the test script and node that works look like the following:
%%% Test Script that works but fails codegen %%%
% Set up test node
% Call the node that I am trying to run codegen on
[objectfindernode, objectfindersub]=objectFinderNode()
% Import test data
% Convert test data into msg1 format
% Send msg1 via test node publisher
% Check msg2 via test node subscriber callback
%%% End of test script %%%
%%% Node that works but fails codegen %%%
function [objectfindernode, objectfindersub]=objectFinderNode()
% Initialize handle objects ob1-obn and run needed methods
% build ros2 node 'objectfindernode'
% Add publisher 'pub' that publishes response 'msg2'
% Add subscriber 'objectfindersub' that subcribes to 'msg1' with callback
% callback1(ob1,...,obn,pub)
end
function callback1(msg,ob1,..,obn,pub)
% Do handle objects ob1-obn methods
% Populate and send msg2 via pub
end
%%% End of node that works but fails codegen %%%
If I change it to the following, the msg2 check never occurs, indicating that callback 1 never happened. Codegen runs on this version, but I still run into the error: 'The library 'ROS 2 Node' is not supported by the current target, language or compiler.' I also do not know which parts of the code are actually getting called.
%%% Test Script that does not work correctly and fails codegen differently %%%
% Set up test node
% Call the node that I am trying to run codegen on
[objectfindernode, objectfindersub]=objectFinderNode()
% Import test data
% Convert test data into msg1 format
% Send msg1 via test node publisher
% Check msg2 via test node subscriber callback
%%% End of test script %%%
%%% Node that can run in codegen until the listed error %%%
function objectFinderNode()
% Initialize handle objects ob1-obn and run needed methods
% build ros2 node 'objectfindernode'
% Add publisher 'pub' that publishes response 'msg2'
% Add subscriber 'objectfindersub' that subcribes to 'msg1' with callback
% callback1(ob1,...,obn,pub)
end
function callback1(msg,ob1,..,obn,pub)
% Do handle objects ob1-obn methods
% Populate and send msg2 via pub
end
%%% End of node %%%
At this point I am stuck. Unless there are more workarounds that I am not aware of this is questionably feasible project, due to the difficulty involved in generating test data for the node with only the codegen-viable set of functions. Also, I have already applied the workaround shown here for how to fix the ros2subscription callback issues within codegen: https://www.mathworks.com/matlabcentral/answers/1851568-ros2-subscriber-callback-function-arguments-for-code-generation
Separately, are there plans to support handle object generation within loops in the future within codegen? I use cell arrays of handle objects throughout my project, so having to initialize each one individually is very tedious and verbose if it is a large array.
  5 Comments
James Croughan
James Croughan on 19 Dec 2023
Hi Josh,
  1. At a high level, I have a whole bunch of nodes that interact with each other via a large system of messages and callback functions, let's call these myNoden (with n=1:N). Each of these has m (m=1:M) callbacks, which we can call callbacknm. Each of these callbacknm's act on a set of custom handle objects that are initialized when each node is created. Each node has no looping portion, and all actions are triggered by the subscription callbacks, which can have complex execution structures. In order to test a system like this, I have to return the ros2 node and subscriber objects that are made by the node generator function (such as objectFinderNode) into the main workspace after they are generated, otherwise they are just deleted after the generator function completes, since there is no looping portion. If I add loops to the generator function, then I can at most test one node at a time, since it will hang on that loop, preventing the initialization of the other nodes. Returning the node and subscriber objects ([objectfindernode, objectfindersub]=objectFinderNode()) for all N nodes into the main workspace allows me to test all the nodes with the needed interactions between them - since they trigger each other via publishers that are included as inputs some of the callbacknm functions.
  2. Yes, but only if that subscriber still exists in the same workspace. If I just call objectFinderNode() with no outputs and no looping portion, the subscibers are deleted on completiong of objectFinderNode, meaning there is nothing to check.
  3. Yes, I have been doing what you have shown there.
Using my workarounds listed previously, I can fully test and debug things first in a normal matlab workspace, and then reformat the callbacks into the needed ros2 format and continue with needed codegen modifications within the coder app using a test script that triggers the callbacks up until the above error is all that remains. At that point, I remove the outputs from the node generator functions, and switch to the script-based version of codegen. The (possible) problem is that when I then run codegen via the cfg method, I have no way to check which of the execution structures within callbacknm functions were checked and converted, since there is no input test script/function. Assuming that all execution paths within the callbacknm function get converted as part of this, then this is a fully valid workaround, but I haven't verified whether or not this is the case yet
Josh Chen
Josh Chen on 11 Jan 2024
Edited: Josh Chen on 11 Jan 2024
Hi James,
Sorry about my late response, not sure why there is no notification forwarded to my email.
Yes, when you mentioned the system has multiple nodes (n=1:N), and they should be able to interact with each other, first time came into my mind is you can use script-based codegen and just run the codegen commmand multiple times to build and run multiple nodes.
I might not fully understand the execution structures you want to check and convert, but it sounds like each node and callback has their own specific parameter/variable, and you want to test all of those combinations.
If we create one function for each node, then it allows you to:
  1. Include infinite loop to keep node alive until you manually kill the node
  2. Use meaningful name to distinguish between different nodes (e.g. first function is the first node, so inside first function, use ros2node('node_1');, in second function, use ros2node('node_2');, etc
  3. Each node run independently and are always visible to other nodes as long as they are in the same domain id.
Hope that helps.
Thanks,
Josh

Sign in to comment.

Answers (0)

Products


Release

R2022b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!