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

Class-based loadable agent params #410

Merged
merged 1 commit into from
May 23, 2018
Merged

Conversation

basicNew
Copy link

Removes the generic dictionary + linb-any to pass parameters to agents and replaces them with agent-specific classes for params.

Currently WIP.

@basicNew basicNew force-pushed the basicnew/loadable-agent-params branch 2 times, most recently from 0965c87 to 72c2aed Compare May 15, 2018 15:18
@basicNew basicNew requested a review from hidmic May 15, 2018 15:18
@basicNew
Copy link
Author

@hidmic for a first pass.

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

@basicNew I left some comments. Most of the new classes seem to be lacking documentation, consider adding some.

const drake::maliput::api::RoadGeometry* road) {
return AddLoadableAgent(plugin_library_name, "", agent_name,
std::move(initial_state), road,
std::move(std::make_unique<AgentPluginParams>()));
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew FYI just so that you know, this move is redundant. But there's no need to change it.

Copy link
Author

Choose a reason for hiding this comment

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

+1, done

#include "../include/delphyne/agent_plugin_base.h"
#include "../src/agents/mobil_car.h"
#include "../src/agents/rail_car.h"
#include "../src/agents/trajectory_car.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew hmmm is this the standard now on Delphyne? It looks.. awkward.

Copy link
Author

Choose a reason for hiding this comment

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

We decided to revert this and remove relative imports. Changed.

template <typename ConcreteAgentParams>
std::unique_ptr<ConcreteAgentParams> downcast_params(
std::unique_ptr<AgentPluginParams> base_params) {
DELPHYNE_DEMAND(base_params.get() != nullptr);
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew why aborting if parameters are null? Isn't that a possible condition? I don't see anything enforcing otherwise before calling downcast_params(). And even then, maybe an exception is a bit less harsh than an abort?

Copy link
Author

Choose a reason for hiding this comment

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

Added guards, thanks


class AgentPluginParams {
public:
virtual ~AgentPluginParams() {}
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew consider making the destructor pure virtual so that this class becomes abstract.

Copy link
Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, that was a suprise for me too. Though you can have = 0 anyways if I follow that blog correctly (so that you enforce the 'abstractness' of the class).

ASSERT_EQ(0, agent->Configure("testname", 0, params, builder.get(), nullptr,
nullptr));
auto params = std::make_unique<delphyne::AgentPluginParams>();
ASSERT_EQ(0, agent->Configure("testname", 0, builder.get(), nullptr, nullptr,
Copy link
Contributor

@hidmic hidmic May 15, 2018

Choose a reason for hiding this comment

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

@basicNew here and below, consider using kNullNameOfTheThing instead of nullptr directly to make this a bit more readable. Same thing with regard to the zero id.

Copy link
Author

Choose a reason for hiding this comment

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

Done, thanks!

: lane_direction_(std::move(lane_direction)),
start_params_(std::move(start_params)) {}

drake::automotive::LaneDirection* get_raw_lane_direction() {
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew may be const, both method and returned pointer (I think).

Copy link
Author

Choose a reason for hiding this comment

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

+1 done

return lane_direction_.get();
}

drake::automotive::MaliputRailcarParams<double>* get_raw_start_params() {
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew may be const, both method and returned pointer (I think).

Copy link
Author

Choose a reason for hiding this comment

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

+1 done

std::unique_ptr<drake::automotive::Curve2<double>> curve)
: curve_(std::move(curve)) {}

drake::automotive::Curve2<double>* get_raw_curve() { return curve_.get(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew may be const, both method and returned pointer (I think).

Copy link
Author

Choose a reason for hiding this comment

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

+1 done

bool get_initial_with_s() { return initial_with_s_; }

private:
bool initial_with_s_{true};
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew I'd remove the in-member initialization of initial_with_s_ and delete the default constructor (as you have no way of setting parameters after construction, I presume you've made them immutable on purpose). The same goes for its derived classes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Forget about above's comment. No default constructor will be implicitly added as there's a constructor defined for the class a few lines above. Had a brain SIGSEGV, sorry.

Copy link
Author

Choose a reason for hiding this comment

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

No worries, thanks for raising it anyway!

/// Throws an exception if the provided parameter is not of the expected
/// class
template <typename ConcreteAgentParams>
std::unique_ptr<ConcreteAgentParams> downcast_params(
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew I wonder if this should be part of AgentPluginParams, with an additional check on ConcreteAgentParams, like:

template<typename ConcreteAgentPluginParams, 
         typename std::enable_if<std::is_base_of<AgentPluginParams, ConcreteAgentPluginParams>::value>::type> 
std::unique_ptr<ConcreteAgentPluginParams> downcast_params(std::unique_ptr<AgentPluginParams> base_params) {...}

Copy link
Author

Choose a reason for hiding this comment

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

+100, like it!

std::unique_ptr<AgentPluginParams> parameters) {
return AddLoadableAgent(plugin_library_name, "", agent_name,
std::move(initial_state), road,
std::move(parameters));
Copy link
Collaborator

Choose a reason for hiding this comment

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

@basicNew how do you feel about dropping this method and coercing the user to always specify both plugin_library_name and plugin_name?

Copy link
Author

Choose a reason for hiding this comment

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

It is actually quite handy to have this; as a matter of fact, we are not using the "full" version of it anywhere :). I would vote for leaving it.

@@ -24,6 +24,7 @@
#include "drake/automotive/simple_car.h"
#include "drake/automotive/trajectory_car.h"
#include "drake/common/drake_copyable.h"
#include "drake/lcm/drake_lcm_interface.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should be able to drop this - went away in #408

Copy link
Author

Choose a reason for hiding this comment

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

+1, rebase leftovers.

py::class_<linb::any>(m, "Any")
.def(py::init<bool&&>())
// Keep alive, ownership: `self` keeps `RoadGeometry` alive.
.def(py::init<const RoadGeometry*&&>(), py::keep_alive<1, 2>())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Even if we go in this direction, you'll probably want to keep anything maliput related (road related) around so that the user can dig into the roads (i.e. world ground truth) in scenario setup, or callback scripts.

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure I follow; is this related to the python bindings discussion we were having on slack or is it a different thing?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was working under the mistaken assumption that the road was instantiated in python. From a look at the PR though, looks like it's getting built by RoadBuilder, which means being instantiated in c++ with ownership transferred to the simulator. This is fine.

Copy link
Author

Choose a reason for hiding this comment

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

Ah, got it.

@basicNew
Copy link
Author

@hidmic thanks a lot for the detailed review. Ready for another pass when you get a chance.

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

@basicNew A few more comments, it's looking great!

@@ -161,6 +161,8 @@ int AutomotiveSimulator<T>::AddLoadableAgent(
if (plugin_name.empty()) {
agent = delphyne::LoadPlugin<T>(plugin_library_name);
} else {
std::cerr << "Plgin with Name!!!!!" << std::endl;
throw std::runtime_error("Plgin with Name!!!!!");
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew I like the emphasized panicking, but a few considerations:

  • Shouldn't it be Plgin -> Plugin?
  • Since you're throwing, code afterwards in the else clause is dead code.
  • I'd rather not write stdout/stderr from within libraries (I personally hate when libraries do that and I have no control over what my application is going to output).

Copy link
Contributor

Choose a reason for hiding this comment

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

According to @basicNew, this is a testing leftover. Removing.

@@ -98,6 +95,8 @@ class AutomotiveSimulator {
const drake::maliput::api::RoadGeometry* road,
std::unique_ptr<AgentPluginParams> parameters);

/// An handy overload for the case that the plugin doesn't require any extra
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew Consider An handy overload for the case that the plugin... -> A handy overload in case the plugin....

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.


class AgentPluginParams {
public:
virtual ~AgentPluginParams() {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, that was a suprise for me too. Though you can have = 0 anyways if I follow that blog correctly (so that you enforce the 'abstractness' of the class).

typename std::enable_if<std::is_base_of<
AgentPluginParams, ConcreteAgentPluginParams>::value>::type* =
nullptr>
std::unique_ptr<ConcreteAgentPluginParams> downcast_params(
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew +10, though I'd make it static or move it outside the class (class is just scoping here, there's no access to the current instance).

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

ignerr << "RailCar::Configure(): "
"The provided initial lane is not within this simulation's "
"RoadGeometry."
<< std::endl;
return -1;
}

if (params_->get_raw_start_params() == nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew I can't seem to find where params_->get_raw_start_params() is used besides here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nevermind, it's used near the bottom of the method definition.

ASSERT_EQ(0, agent->Configure("testname", 0, builder.get(), nullptr, nullptr,
nullptr, std::move(params)));
ASSERT_EQ(
0, agent->Configure("testname", 0, builder.get(), kNullPoseAggregator,
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew here and below, those 0s could be named too e.g. kSuccessCode and kDefaultID respectively.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.


TEST(AgentPluginLoader, Invalid) {
auto agent = delphyne::LoadPlugin<double>("foo");
ASSERT_EQ(nullptr, agent);
}

static const char* env = "DELPHYNE_AGENT_PLUGIN_PATH=agent_plugin";
static const drake::maliput::api::RoadGeometry* kNullRoad{nullptr};

TEST(AgentPluginLoader, SimpleAgent) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew I've just noticed that all three tests are identical except for a type. If there're no plans for this to be different, consider using typed tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.


TEST(AgentPluginLoader, Invalid) {
auto agent = delphyne::LoadPlugin<double>("foo");
ASSERT_EQ(nullptr, agent);
}

static const char* env = "DELPHYNE_AGENT_PLUGIN_PATH=agent_plugin";
static const drake::maliput::api::RoadGeometry* kNullRoad{nullptr};
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew IMHO the last two lines should go like static const type* const name.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

ASSERT_EQ(-1, simulator->AddLoadableAgent("NonExistentPlugin",
"my_test_model", nullptr, nullptr));
auto state = std::make_unique<SimpleCarState<double>>();
ASSERT_EQ(-1,
Copy link
Contributor

Choose a reason for hiding this comment

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

@basicNew same as above regarding -1 e.g. kErrorCode.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

@hidmic hidmic force-pushed the basicnew/loadable-agent-params branch from 7e7d5de to 82a64dd Compare May 22, 2018 21:07
@hidmic
Copy link
Contributor

hidmic commented May 22, 2018

I had to rebase against master, there was a dependency change along the way.

@hidmic
Copy link
Contributor

hidmic commented May 22, 2018

I feel bad approving myself, @basicNew @apojomovsky mind to take a look?

@basicNew
Copy link
Author

@hidmic thanks for finishing this one! Couple of minor things/checks:

  • Could you squash the last two commits? They are both "Address comments".
  • In case you missed any of them, there are tools/reformat_code.sh, tools/run_cpplint.sh and tools/run_python_checks.sh that should be checked.

Aside from that it is good to go!

@hidmic hidmic force-pushed the basicnew/loadable-agent-params branch from 82a64dd to 9bfaa20 Compare May 23, 2018 14:00
@hidmic
Copy link
Contributor

hidmic commented May 23, 2018

@basicNew Commits and curated and tools run (I wonder if we should have commit hooks, though I bet that was discussed before). Also, it seems that pylint doesn't play along with the delphyne.bindings module. We might want to fix/workaround that later on.

@basicNew
Copy link
Author

@hidmic yup, known issue with pylint. Feel free to merge once tests are green. Thanks!

@hidmic hidmic merged commit acd4b13 into master May 23, 2018
@stonier stonier deleted the basicnew/loadable-agent-params branch May 24, 2018 02:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants