How to programmatically access (and edit) bus elements

295 views (last 30 days)
My project has a restriction that bus elements need to be all lowercase. The architecture has bus objects stored in data dictionaries, and the model uses these in BusCreator objects.
In order to enforce compliance with this restriction, I'd like to programmatically inspect all the buses used in a model, and/or bus objects stored in the data dictionaries. When an offending element is found, I might
  1. just print an error message,
  2. provide a dialog to help the user edit the name, or
  3. do some kind of automatic name change.
But the first issue is to identify the uppercase and camelcase bus elements.
So far, here's what has not worked for me:
1. Inspecting the BusCreator
I've tried selecting a BusCreator block which has elements that need to be corrected, then using the command
get_param(gcb,'DialogParameters')
but the result doesn't contain the bus name, much less the element names.
2. Finding Bus Definitions used by the model
This gets me a little closer; I can use
modelVariables = Simulink.findVars(bdroot,'SourceType','data dictionary')
then check each of the modelVariables.Users to see if that user block is a BusCreator; in which case the variable is probably the name of a bus object. But once I have the bus names, I still need to find the element names, and I'm stumped there.
3. Finding Bus Definitions in the SLDD
I've tried using the Simulink.data.Dictionary class methods to find bus objects' elements names, but haven't found a way to actually find the bus element names, much less edit them.
4. Model Advisor Checks
I took a brief look, but didn't see a way to customize mathworks.maab.jc_0221 Check character usage in signal labels to flag uppercase characters. I don't necessarily want to exclude uppercase on ALL signals, although doing that would probably end up helping achieve all lowercase bus elements.

Accepted Answer

John Harris
John Harris on 10 Feb 2017
Edited: John Harris on 14 Feb 2017
I wanted to throw a brief update out there about what I ended up with. Thanks to Shivang Menon @ mathworks for some technical support and guidance to use arrayfun & cellfun, I ended up getting there. I won't post all the code I went through because it was scattered across several functions, but here are the key parts to accessing the element names:
sldd_object = Simulink.data.dictionary.open('filename.sldd');
section = getSection(sldd_object, 'Design Data');
entries = find(section, '-value', '-class', 'Simulink.Bus');
% produce cell array of Simulink.Bus objects:
buses = arrayfun(@(x) getValue(x), entries, 'UniformOutput', false);
% produce a cell array of cell arrays of Simulink.BusElement objects:
elements = cellfun(@(x) x.Elements, buses, 'UniformOutput', false);
% produce a cell array of cell arrays of element names:
element_names = getElementNames(elements);
function names = getElementNames(arrayOfElements)
%%getElementNames returns a cell array of element names
function names = busElementNames(busElement)
if (length(busElement)<1)
errordlg('There is a bus with zero elements. Oops.')
elseif (length(busElement)==1)
names{1}{1} = busElement.Name;
else
names = arrayfun(@(x) x.Name, busElement, 'UniformOutput', false);
end
end
names = cellfun(@busElementNames, arrayOfElements, 'UniformOutput', false);
end
  3 Comments
John Harris
John Harris on 14 Feb 2017
Only when getElementNames() is processing a bus that has one element. I'm passing a cell array of cell arrays of Simulink.BusElement objects into getElementNames() and I had to cover the case of buses with 0 elements, 1 element, and multiple elements, too:
if (length(busElement)<1)
errordlg('There is a bus with zero elements. Oops.')
elseif (length(busElement)==1)
names{1}{1} = busElement.Name;
else
names = arrayfun(@(x) x.Name, busElement, 'UniformOutput', false);
end
Walter Roberson
Walter Roberson on 14 Feb 2017
busElement appears to be a structure array at that point in the code. If so then the code could be
names = {busElement.Name};
which would cover the case of 1 element or more elements and would generate {} for the case where it was empty (instead of an error.)
If busElement is not a struct array at that point then it would have to be an array of objects and x.Name would have to be fetching a Property... I guess perhaps that might not happen to support structure expansion.

Sign in to comment.

More Answers (2)

Walter Roberson
Walter Roberson on 23 Dec 2016
Playing around a bit, and looking at the Block Specific Parameters documentation of get_param, I see that Bus Selector blocks have a property InputSignals which are the names of the signals on the bus.
I also see that get_param() of 'OutputSignalNames' on a Bus Creator object gives the bus name.
However at the moment I do not know how to find all the Bus Creator or Bus Selector objects.
Ah...
bus_creator_info = find_system(bdroot, 'BlockType','BusCreator');
num_bus = length(bus_creator_info);
bus_name = cell(num_bus, 1);
for K = 1 : num_bus
bus_name{K} = get_param(bus_creator_info{K}, 'OutputSignalNames');
end
bus_selector_info = find_system(bdroot, 'BlockType','BusSelector');
num_bus = length(bus_selector_info);
bus_name = cell(num_bus, 1);
for K = 1 : num_bus
bus_signals{K} = get_param(bus_selector_info{K}, 'InputSignals');
end
You could clearly adapt this to look for particular characters in the signal names.
  2 Comments
John Harris
John Harris on 23 Dec 2016
Edited: John Harris on 23 Dec 2016
BusSelector
The models may not have a BusSelector anywhere on a bus that gets produced in the model....I need to check the buses as they are created.
BusCreator
Hmm; the OutputSignalNames of a BusCreator object is the signal name of the output bus, not the bus data type. But along your suggestion, it looks like I can get at the data type using the block property OutDataTypeStr:
busCreatorHandles = find_system(bdroot(gcbh),'LookUnderMasks','all','BlockType','BusCreator');
busTypes = get_param(busCreatorHandles,'OutDataTypeStr');
busDataTypes = strrep(busTypes,'Bus: ','');
which gives me a cell array of the data types of each bus creator object.
Now I still need to find the bus element names...I'm thinking I should be able to parse the bus definition inside a data dictionary once I have the bus name, but was hitting a brick wall trying to do that yesterday.
Alireza Tajafari Sahebi
Alireza Tajafari Sahebi on 11 May 2022
Edited: Alireza Tajafari Sahebi on 11 May 2022
To get the bus elements name:
get_param (enter your bus name here, 'PortConnectivity')
After runing, you will have a structure in your workspace. This structure has 6 fields. One of these fields is SrcBlock.
SrcBlock includs all handles of all input signals of your bus creator.

Sign in to comment.


Nagendra
Nagendra on 11 Apr 2024 at 2:41
Edited: Nagendra on 11 Apr 2024 at 2:47
Hi John,
"3. Finding Bus Definitions in the SLDD
I've tried using the Simulink.data.Dictionary class methods to find bus objects' elements names, but haven't found a way to actually find the bus element names, much less edit them"
I managed to do it with Simulink.data.Dictionary class method, may be useful:
Assumption: data dictionary with name 'myDictionary.sldd' exists and contains a bus object named 'mybus1'
%% Open the data dictionary (ssuming a data dictionary is already created) and move the newly created bus object to the data dictionray
myDictionaryObj = Simulink.data.dictionary.open('myDictionary.sldd'); % open the data dictionary
dDataSectObj = getSection(myDictionaryObj,'Design Data'); % access the Design Data section
%% Get the bus object details and assign the dimension variable to Dimensions filed
busobject_details = getEntry(dDataSectObj,'mybus1'); % get bus object details
bus_objval=getValue(busobject_details); % get the values of bus object
element_size=length(bus_objval.Elements);
elem_names=strings(1,element_size); % empty string array to hold the element names
for i=1:element_size
elem_names(1,i)=convertCharsToStrings(bus_objval.Elements(i).Name);% get the names of elements
end
%% Carryout your customization based on the found element names
%dummy code
% edit and update the bus element names to data dictionary
new_names = ["new_name1" "new_name2"]; % 'mybus1' has 2 elements and both elements name to be changed, this array can vary depending upon the size of elements whose name to be changed
for j=1:element_size % here instead of element size, size of elements whose names should be changed should be assigned
bus_objval.Elements(j).Name=new_names(j); % assign new name to element 1
%bus_objval.Elements(2).Name='new_name2';% assign new name to element 2
end
%Note: A for loop can be defined to pass the new names of elements from an
%array
setValue(busobject_details,bus_objval); % update the data dictionary entry

Categories

Find more on Composite Interfaces in Help Center and File Exchange

Products

Community Treasure Hunt

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

Start Hunting!