Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nested variable groups #537

Merged
merged 5 commits into from
Mar 27, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions src/cpp/cse_config_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ struct variable_group_description
std::string name;
std::string type;
std::vector<std::string> variables;
std::vector<variable_group_description> variable_group_descriptions;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think groups or subgroups would be a more descriptive name for this.

};

struct extended_model_description
Expand All @@ -582,19 +583,28 @@ struct extended_model_description
const auto variableGroupsElement = static_cast<xercesc::DOMElement*>(rootElement->getElementsByTagName(tc("VariableGroups").get())->item(0));

for (auto variableGroupElement = variableGroupsElement->getFirstElementChild(); variableGroupElement != nullptr; variableGroupElement = variableGroupElement->getNextElementSibling()) {
variable_group_description vgd;
vgd.name = tc(variableGroupElement->getAttribute(tc("name").get())).get();
vgd.type = tc(variableGroupElement->getAttribute(tc("type").get())).get();
variable_group_description vgd = create_variable_group_description(variableGroupElement);
variableGroups[vgd.name] = std::move(vgd);
}
}

auto variableElements = variableGroupElement->getElementsByTagName(tc("Variable").get());
for (size_t i = 0; i < variableElements->getLength(); i++) {
auto variableElement = static_cast<xercesc::DOMElement*>(variableElements->item(i));
static variable_group_description create_variable_group_description(xercesc::DOMElement *variableGroupElement) {
variable_group_description variableGroupDescription;

std::string variableName = tc(variableElement->getAttribute(tc("name").get())).get();
vgd.variables.push_back(std::move(variableName));
variableGroupDescription.name = tc(variableGroupElement->getAttribute(tc("name").get())).get();
variableGroupDescription.type = tc(variableGroupElement->getTagName()).get();

for (auto variableGroupChildElement = variableGroupElement->getFirstElementChild(); variableGroupChildElement != nullptr; variableGroupChildElement = variableGroupChildElement->getNextElementSibling()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some reeeeally long lines of code in this file, and it's actually a bit tricky to see the end of lines like this one due to how GitHub places the horizontal scroll bar.

I'm not going to suggest that we use this PR to reformat the entire file, but it would be nice if added/modified code was made a tad more readable. ;)

std::string tagName(tc(variableGroupChildElement->getTagName()).get());
if (tagName == "Variable") {
std::string variableName = tc(variableGroupChildElement->getAttribute(tc("ref").get())).get();
variableGroupDescription.variables.push_back(std::move(variableName));
} else {
variableGroupDescription.variable_group_descriptions.push_back(create_variable_group_description(variableGroupChildElement));
}
variableGroups[vgd.name] = std::move(vgd);
}

return variableGroupDescription;
}

std::unordered_map<std::string, variable_group_description> variableGroups;
Expand Down Expand Up @@ -722,13 +732,35 @@ std::vector<std::string> get_variable_group_variables(
{
auto groupIt = descriptions.find(connector);
if (groupIt == descriptions.end()) {
for (const auto& description : descriptions) {
if (description.second.variable_group_descriptions.size() != 0) {
std::unordered_map<std::string, variable_group_description> nestedDescriptions;
for (const auto& variableGroupDescr : description.second.variable_group_descriptions) {
nestedDescriptions.insert({variableGroupDescr.name, variableGroupDescr});
}
return get_variable_group_variables(nestedDescriptions, element, connector);
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't like to build up the nestedDescription map here. If the variable_goup_decsriptions in the variable_group_description struct (line 560) was a map instead of a vector, we could have this recursion without building up temporary maps.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Is there any reason to not make it a map?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Just didn't want to make changes in the parser now. Let's do this as a separate refactoring task for the ´cse_config_parser´

std::ostringstream oss;
oss << "Cannot find variable group description: " << element << ":" << connector;
throw std::out_of_range(oss.str());
}
return groupIt->second.variables;
}

std::vector<cse::variable_group_description> get_variable_groups(
const std::unordered_map<std::string, variable_group_description>& descriptions,
const std::string& connector)
{
auto groupIt = descriptions.find(connector);
if (groupIt == descriptions.end()) {
std::ostringstream oss;
oss << "Cannot find variable group for " << connector;
throw std::out_of_range(oss.str());
}
return groupIt->second.variable_group_descriptions;
}

void connect_variable_groups(
const std::vector<cse_config_parser::VariableConnection>& variableGroupConnections,
const std::unordered_map<std::string, slave_info>& slaves,
Expand All @@ -744,6 +776,23 @@ void connect_variable_groups(
const auto& variablesB =
get_variable_group_variables(emdB.variableGroups, connection.variableB.simulator, connection.variableB.name);

if (variablesA.size() == 0 || variablesB.size() == 0) {
// Variables for the connection cannot be found directly under any of the variable groups.
// The connection refers to a variableGroup with variableGroups.
const auto& variableGroupsA = get_variable_groups(emdA.variableGroups, connection.variableA.name);
const auto& variableGroupsB = get_variable_groups(emdB.variableGroups, connection.variableB.name);

std::vector<cse_config_parser::VariableConnection> nestedVariableGroupConnections;
// clang-format off
for (std::size_t i = 0; i < variableGroupsA.size(); ++i) {
nestedVariableGroupConnections.push_back({
{connection.variableA.simulator, variableGroupsA.at(i).name},
{connection.variableB.simulator, variableGroupsB.at(i).name}});
}
// clang-format on
connect_variable_groups(nestedVariableGroupConnections, slaves, execution, emds);
}

if (variablesA.size() != variablesB.size()) {
std::ostringstream oss;
oss << "Cannot create connection between variable groups. Variable group "
Expand Down
2 changes: 2 additions & 0 deletions test/cpp/cse_config_parser_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ void test(const boost::filesystem::path& configPath, size_t expectedNumConnectio
REQUIRE(simulator_map.at("KnuckleBoomCrane").source == "../ssp/demo/KnuckleBoomCrane.fmu");

const auto& connections = execution.get_connections();
std::cout << connections.size() << std::endl;
ljamt marked this conversation as resolved.
Show resolved Hide resolved
std::cout << expectedNumConnections << std::endl;
REQUIRE(connections.size() == expectedNumConnections);
for (const auto& connection : connections) {
if (const auto sum = std::dynamic_pointer_cast<cse::sum_connection>(connection)) {
Expand Down
32 changes: 17 additions & 15 deletions test/data/msmi/CraneController_OspModelDescription.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@
</Unit>
</UnitDefinitions>
<VariableGroups>
<VariableGroup type="linear_velocity" name="velocity">
<Variable name="p_Crane.f[1]" unit="velocity~m_s"/>
<Variable name="p_Crane.f[2]" unit="velocity~m_s"/>
<Variable name="p_Crane.f[3]" unit="velocity~m_s"/>
</VariableGroup>
<VariableGroup type="generic" name="actuatorLimits">
<Variable name="Act_Limits[1]"/>
<Variable name="Act_Limits[2]"/>
<Variable name="Act_Limits[3]"/>
</VariableGroup>
<VariableGroup type="force" name="force">
<Variable name="p_Crane.e[1]" unit="force~N"/>
<Variable name="p_Crane.e[2]" unit="force~N"/>
<Variable name="p_Crane.e[3]" unit="force~N"/>
</VariableGroup>
<Generic name="actuatorLimits">
<Variable ref="Act_Limits[1]"/>
<Variable ref="Act_Limits[2]"/>
<Variable ref="Act_Limits[3]"/>
</Generic>
<LinearMechanicalPort name="linear mechanical port">
<Force name="force">
<Variable ref="p_Crane.e[1]" unit="force~N"/>
<Variable ref="p_Crane.e[2]" unit="force~N"/>
<Variable ref="p_Crane.e[3]" unit="force~N"/>
</Force>
<LinearVelocity name="velocity">
<Variable ref="p_Crane.f[1]" unit="velocity~m_s"/>
<Variable ref="p_Crane.f[2]" unit="velocity~m_s"/>
<Variable ref="p_Crane.f[3]" unit="velocity~m_s"/>
</LinearVelocity>
</LinearMechanicalPort>
</VariableGroups>
</OspModelDescription>
34 changes: 18 additions & 16 deletions test/data/msmi/KnuckleBoomCrane_OspModelDescription.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@
<OspModelDescription xmlns="http://opensimulationplatform.com/MSMI/OSPModelDescription"
version="0.1">
<VariableGroups>
<VariableGroup type="force" name="force">
<Variable name="p_Crane.e[1]"/>
<Variable name="p_Crane.e[2]"/>
<Variable name="p_Crane.e[3]"/>
</VariableGroup>
<VariableGroup type="linear_velocity" name="velocity">
<Variable name="p_Crane.f[1]"/>
<Variable name="p_Crane.f[2]"/>
<Variable name="p_Crane.f[3]"/>
</VariableGroup>
<VariableGroup type="generic" name="actuatorLimits">
<Variable name="Act_Limits[1]"/>
<Variable name="Act_Limits[2]"/>
<Variable name="Act_Limits[3]"/>
</VariableGroup>
</VariableGroups>
<Generic name="actuatorLimits">
<Variable ref="Act_Limits[1]"/>
<Variable ref="Act_Limits[2]"/>
<Variable ref="Act_Limits[3]"/>
</Generic>
<LinearMechanicalPort name="linear mechanical port">
<Force name="force">
<Variable ref="p_Crane.e[1]"/>
<Variable ref="p_Crane.e[2]"/>
<Variable ref="p_Crane.e[3]"/>
</Force>
<LinearVelocity name="velocity">
<Variable ref="p_Crane.f[1]"/>
<Variable ref="p_Crane.f[2]"/>
<Variable ref="p_Crane.f[3]"/>
</LinearVelocity>
</LinearMechanicalPort>
</VariableGroups>
</OspModelDescription>
9 changes: 2 additions & 7 deletions test/data/msmi/OspSystemStructure_Bond.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,9 @@
<VariableGroup simulator="KnuckleBoomCrane" name="actuatorLimits"/>
<VariableGroup simulator="CraneController" name="actuatorLimits"/>
</VariableGroupConnection>
<!-- Splitting the bond into two -->
<VariableGroupConnection>
<VariableGroup simulator="KnuckleBoomCrane" name="velocity"/>
<VariableGroup simulator="CraneController" name="velocity"/>
</VariableGroupConnection>
<VariableGroupConnection>
<VariableGroup simulator="KnuckleBoomCrane" name="force"/>
<VariableGroup simulator="CraneController" name="force"/>
<VariableGroup simulator="KnuckleBoomCrane" name="linear mechanical port"/>
<VariableGroup simulator="CraneController" name="linear mechanical port"/>
</VariableGroupConnection>
</Connections>
</OspSystemStructure>
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<OspModelDescription xmlns="http://opensimulationplatform.com/MSMI/OSPModelDescription">
<VariableGroups>
<VariableGroup type="generic" name="input">
<Variable name="input[0]"/>
<Variable name="input[1]"/>
<Variable name="input[2]"/>
</VariableGroup>
<VariableGroup type="generic" name="output">
<Variable name="output[0]"/>
<Variable name="output[1]"/>
<Variable name="output[2]"/>
</VariableGroup>
<Generic name="input">
<Variable ref="input[0]"/>
<Variable ref="input[1]"/>
<Variable ref="input[2]"/>
</Generic>
<Generic name="output">
<Variable ref="output[0]"/>
<Variable ref="output[1]"/>
<Variable ref="output[2]"/>
</Generic>
</VariableGroups>
</OspModelDescription>
94 changes: 46 additions & 48 deletions test/data/msmi/schema/OspModelDescription.xsd
Original file line number Diff line number Diff line change
@@ -1,68 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="http://opensimulationplatform.com/MSMI/OSPModelDescription"
xmlns:osp="http://opensimulationplatform.com/MSMI/OSPModelDescription"
xmlns="http://opensimulationplatform.com/MSMI/OSPModelDescription"
version="0.1">
<xs:include schemaLocation="fmi2Unit.xsd"/>
<xs:element name="OspModelDescription" type="osp:OspModelDescriptionType"/>
<xs:element name="OspModelDescription" type="OspModelDescriptionType"/>
<xs:complexType name="UnitDefinitionsType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element type="osp:fmi2Unit" name="Unit"/>
<xs:element type="fmi2Unit" name="Unit"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="VariableType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="ref" use="required"/>
<xs:attribute type="xs:string" name="unit"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="ForceType">
<xs:sequence>
<xs:element type="VariableType" name="Variable" maxOccurs="3" minOccurs="1"/>
</xs:sequence>
<xs:attribute type="xs:string" name="name" use="required"/>
<xs:attribute type="xs:string" name="unit" use="optional"/>
</xs:complexType>
<xs:complexType name="VariableGroupType">
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element type="osp:VariableType" name="Variable"/>
<xs:element type="osp:VariableGroupType" name="VariableGroup"/>
</xs:choice>
<xs:complexType name="LinearVelocityType">
<xs:sequence>
<xs:element type="VariableType" name="Variable" maxOccurs="3" minOccurs="1"/>
</xs:sequence>
<xs:attribute type="xs:string" name="name" use="required"/>
<xs:attribute name="type" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="generic"/>

<xs:enumeration value="force"/>
<xs:enumeration value="linear_velocity"/>
<xs:enumeration value="linear_displacement"/>

<xs:enumeration value="torque"/>
<xs:enumeration value="angular_velocity"/>
<xs:enumeration value="angular_displacement"/>

<xs:enumeration value="voltage"/>
<xs:enumeration value="current"/>
<xs:enumeration value="charge"/>

<xs:enumeration value="pressure"/>
<xs:enumeration value="volume_rate_flow"/>
<xs:enumeration value="volume"/>

<xs:enumeration value="linear_mechanical_port"/>
<xs:enumeration value="angular_mechanical_port"/>
<xs:enumeration value="electromagnetic_port"/>
<xs:enumeration value="hydraulic_port"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<xs:complexType name="VariableGroupsType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element type="osp:VariableGroupType" name="VariableGroup"/>
<xs:complexType name="LinearMechanicalPortType">
<xs:sequence>
<xs:element type="ForceType" name="Force"/>
<xs:element type="LinearVelocityType" name="LinearVelocity"/>
</xs:sequence>
<xs:attribute type="xs:string" name="name" use="required"/>
</xs:complexType>
<xs:complexType name="GenericType">
<xs:complexContent>
<xs:extension base="VariableGroupSequenceType">
<xs:choice>
<xs:element type="VariableType" name="Variable" maxOccurs="unbounded" minOccurs="0"/>
</xs:choice>
<xs:attribute type="xs:string" name="name" use="required"/>
</xs:extension>
</xs:complexContent>

</xs:complexType>
<xs:complexType name="VariableGroupSequenceType">
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element type="ForceType" name="Force"/>
<xs:element type="LinearMechanicalPortType" name="LinearMechanicalPort"/>
<xs:element type="LinearVelocityType" name="LinearVelocity"/>
<xs:element type="GenericType" name="Generic"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="OspModelDescriptionType">
<xs:sequence>
<xs:element type="osp:UnitDefinitionsType" name="UnitDefinitions" minOccurs="0"/>
<xs:element type="osp:VariableGroupsType" name="VariableGroups"/>
<xs:element type="UnitDefinitionsType" name="UnitDefinitions" minOccurs="0"/>
<xs:element type="VariableGroupSequenceType" name="VariableGroups"/>
</xs:sequence>
<xs:attribute name="version" type="xs:string" use="required" fixed="0.1">
<xs:annotation>
<xs:documentation>OspModelDescription version</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute type="xs:string" name="version"/>
</xs:complexType>
</xs:schema>
</xs:schema>