Skip to content

Commit

Permalink
[Simulation] Trigger error when Node already contains component
Browse files Browse the repository at this point in the history
  • Loading branch information
alxbilger committed Jan 10, 2025
1 parent 88320b8 commit e877c41
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
19 changes: 18 additions & 1 deletion Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1024,8 +1024,25 @@ void Node::setSleeping(bool val)
}
}

template<class LinkType, class Component>
void checkAlreadyContains(Node& self, LinkType& link, Component* obj)
{
if constexpr (!LinkType::IsMultiLink)
{
if (link != obj && link != nullptr)
{
static const auto componentClassName = Component::GetClass()->className;
msg_error(&self) << "Trying to add a " << componentClassName << " ('" << obj->getName() << "' " << obj << ")"
<< " into the Node '" << self.getPathName()
<< "', whereas it already contains one ('" << link->getName() << "' " << link.get() << ")."
<< " Only one " << componentClassName << " is permitted in a Node. The previous "
<< componentClassName << " is replaced and the behavior is undefined.";
}
}
}

#define NODE_DEFINE_SEQUENCE_ACCESSOR( CLASSNAME, FUNCTIONNAME, SEQUENCENAME ) \
void Node::add##FUNCTIONNAME( CLASSNAME* obj ) { SEQUENCENAME.add(obj); } \
void Node::add##FUNCTIONNAME( CLASSNAME* obj ) { checkAlreadyContains(*this, SEQUENCENAME, obj); SEQUENCENAME.add(obj); } \
void Node::remove##FUNCTIONNAME( CLASSNAME* obj ) { SEQUENCENAME.remove(obj); }

NODE_DEFINE_SEQUENCE_ACCESSOR( sofa::core::behavior::BaseAnimationLoop, AnimationLoop, animationManager )
Expand Down
55 changes: 55 additions & 0 deletions Sofa/framework/Simulation/Graph/test/Node_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
* *
* Contact information: [email protected] *
******************************************************************************/
#include <sofa/core/behavior/BaseMechanicalState.h>
#include <sofa/simulation/MechanicalVisitor.h>
#include <sofa/testing/BaseSimulationTest.h>
using sofa::testing::BaseSimulationTest ;

Expand All @@ -33,6 +35,9 @@ namespace sofa

TEST( Node_test, getPathName)
{
// required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT
sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ;

/* create trivial DAG :
*
* A
Expand Down Expand Up @@ -90,6 +95,9 @@ TEST(Node_test, addObjectAtFront)

TEST(Node_test, addObjectPreventingSharedContext)
{
// required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT
sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ;

const sofa::core::sptr<Node> root = sofa::simpleapi::createNode("root");

const BaseObject::SPtr A = core::objectmodel::New<Dummy>("A");
Expand Down Expand Up @@ -177,6 +185,53 @@ TEST(Node_test, getObjectsStdUnorderedSet)
EXPECT_NE(objects.find(B.get()), objects.end());
}

class CounterVisitor : public simulation::MechanicalVisitor
{
public:
using MechanicalVisitor::MechanicalVisitor;

Result fwdMechanicalState(simulation::Node* node, sofa::core::behavior::BaseMechanicalState* state) override
{
SOFA_UNUSED(node);
SOFA_UNUSED(state);
m_counter++;
return Result::RESULT_CONTINUE;
}

Result fwdMappedMechanicalState(simulation::Node* node, sofa::core::behavior::BaseMechanicalState* state) override
{
SOFA_UNUSED(node);
SOFA_UNUSED(state);
++m_counter;
return Result::RESULT_CONTINUE;
}

std::size_t m_counter = 0;
};

TEST(Node_test, twoMechanicalStatesInTheSameNode)
{
// required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT
sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ;

const sofa::core::sptr<Node> root = sofa::simpleapi::createNode("root");

const auto plugins = testing::makeScopedPlugin({Sofa.Component.StateContainer});
sofa::simpleapi::createObject(root, "MechanicalObject", {{"template", "Vec3"}, {"name", "A"}});

EXPECT_MSG_EMIT(Error);
sofa::simpleapi::createObject(root, "MechanicalObject", {{"template", "Vec3"}, {"name", "B"}});

//the last added state is the one in Node
EXPECT_EQ(root->mechanicalState->getName(), "B");

CounterVisitor visitor(core::MechanicalParams::defaultInstance());
root->executeVisitor(&visitor);

//only one of the two added states is visited
EXPECT_EQ(visitor.m_counter, 1);
}

}// namespace sofa


Expand Down
3 changes: 1 addition & 2 deletions Sofa/framework/Simulation/Graph/test/Simulation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,9 @@ struct Scene_test: public NumericTest<SReal>
root = simulation::getSimulation()->createNewGraph("root");
root->addObject(core::objectmodel::New<InstrumentedObject<MechanicalObject3> >());

typename UniformMass3::SPtr uniformMass = core::objectmodel::New<UniformMass3>();
typename UniformMass3::SPtr uniformMass = core::objectmodel::New<InstrumentedObject<UniformMass3>>();
uniformMass->d_totalMass.setValue(1.0);
root->addObject(uniformMass);
root->addObject(core::objectmodel::New<InstrumentedObject<UniformMass3> >());

const simulation::Node::SPtr child = simulation::getSimulation()->createNewNode("child");
root->addChild(child);
Expand Down

0 comments on commit e877c41

Please sign in to comment.